From 3003178f9591cef0ad1bd0155bdff87842941d31 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 12 Jul 2021 15:57:07 +0200 Subject: [PATCH 001/145] Introduced time evolution classes and interfaces. --- .../quantum_time_evolution/__init__.py | 11 +++++++ .../builders/__init__.py | 11 +++++++ .../builders/evolution_op_builder.py | 21 ++++++++++++ .../builders/implementations/__init__.py | 11 +++++++ .../circuit_evolution_op_builder.py | 17 ++++++++++ .../exact_evolution_op_builder.py | 17 ++++++++++ .../trotterizations/__init__.py | 11 +++++++ .../quantum_time_evolution/evolution_base.py | 32 +++++++++++++++++++ .../imaginary/__init__.py | 11 +++++++ .../imaginary/implementations/__init__.py | 11 +++++++ .../imaginary/implementations/numpy_qite.py | 16 ++++++++++ .../imaginary/implementations/var_qite.py | 17 ++++++++++ .../quantum_time_evolution/imaginary/qite.py | 32 +++++++++++++++++++ .../quantum_time_evolution/real/__init__.py | 11 +++++++ .../real/implementations/__init__.py | 11 +++++++ .../real/implementations/numpy_qrte.py | 16 ++++++++++ .../real/implementations/projected_vqd.py | 16 ++++++++++ .../implementations/trotterization_qrte.py | 16 ++++++++++ .../real/implementations/var_qrte.py | 17 ++++++++++ .../quantum_time_evolution/real/qrte.py | 32 +++++++++++++++++++ .../results/__init__.py | 11 +++++++ .../results/evolution_gradient_result.py | 13 ++++++++ .../results/evolution_result.py | 13 ++++++++ .../variational/__init__.py | 11 +++++++ .../variational/principles/__init__.py | 11 +++++++ .../principles/imaginary/__init__.py | 11 +++++++ .../imaginary_variational_principle.py | 17 ++++++++++ .../imaginary/implementations/__init__.py | 11 +++++++ .../variational/principles/real/__init__.py | 11 +++++++ .../real/implementations/__init__.py | 11 +++++++ .../real/real_variational_principle.py | 17 ++++++++++ .../principles/variational_principle.py | 16 ++++++++++ .../variational/var_qte.py | 16 ++++++++++ .../quantum_time_evolution/__init__.py | 11 +++++++ .../builders/__init__.py | 11 +++++++ .../builders/implementations/__init__.py | 11 +++++++ .../trotterizations/__init__.py | 11 +++++++ 37 files changed, 550 insertions(+) create mode 100644 qiskit/algorithms/quantum_time_evolution/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/evolution_base.py create mode 100644 qiskit/algorithms/quantum_time_evolution/imaginary/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/imaginary/implementations/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/imaginary/implementations/numpy_qite.py create mode 100644 qiskit/algorithms/quantum_time_evolution/imaginary/implementations/var_qite.py create mode 100644 qiskit/algorithms/quantum_time_evolution/imaginary/qite.py create mode 100644 qiskit/algorithms/quantum_time_evolution/real/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/real/implementations/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/real/implementations/numpy_qrte.py create mode 100644 qiskit/algorithms/quantum_time_evolution/real/implementations/projected_vqd.py create mode 100644 qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization_qrte.py create mode 100644 qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py create mode 100644 qiskit/algorithms/quantum_time_evolution/real/qrte.py create mode 100644 qiskit/algorithms/quantum_time_evolution/results/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py create mode 100644 qiskit/algorithms/quantum_time_evolution/results/evolution_result.py create mode 100644 qiskit/algorithms/quantum_time_evolution/variational/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py create mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/implementations/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/real/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/real/implementations/__init__.py create mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py create mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/variational_principle.py create mode 100644 qiskit/algorithms/quantum_time_evolution/variational/var_qte.py create mode 100644 test/python/algorithms/quantum_time_evolution/__init__.py create mode 100644 test/python/algorithms/quantum_time_evolution/builders/__init__.py create mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/__init__.py create mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py diff --git a/qiskit/algorithms/quantum_time_evolution/__init__.py b/qiskit/algorithms/quantum_time_evolution/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/builders/__init__.py b/qiskit/algorithms/quantum_time_evolution/builders/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py new file mode 100644 index 000000000000..da92f7f3d697 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py @@ -0,0 +1,21 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from abc import ABC, abstractmethod + +from qiskit.opflow import OperatorBase + + +class EvolutionOpBuilder(ABC): + + @abstractmethod + def build(self, operator: OperatorBase) -> OperatorBase: + raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/__init__.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py new file mode 100644 index 000000000000..80150de937e5 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py @@ -0,0 +1,17 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import \ + EvolutionOpBuilder + + +class CircuitEvolutionOpBuilder(EvolutionOpBuilder): + pass diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py new file mode 100644 index 000000000000..4d39717247c9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py @@ -0,0 +1,17 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import \ + EvolutionOpBuilder + + +class ExactEvolutionOpBuilder(EvolutionOpBuilder): + pass \ No newline at end of file diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/evolution_base.py b/qiskit/algorithms/quantum_time_evolution/evolution_base.py new file mode 100644 index 000000000000..9adb35b971bc --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/evolution_base.py @@ -0,0 +1,32 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from abc import ABC, abstractmethod + +from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import \ + EvolutionGradientResult +from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult +from qiskit.opflow import OperatorBase, StateFn + + +class EvolutionBase(ABC): + + @abstractmethod + def evolve(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn = None, + observable: OperatorBase = None, t_param=None, + hamiltonian_value_dict=None) -> EvolutionResult: + raise NotImplementedError() + + @abstractmethod + def gradient(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn, + observable: OperatorBase = None, t_param=None, + hamiltonian_value_dict=None, gradient_params=None) -> EvolutionGradientResult: + raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/__init__.py b/qiskit/algorithms/quantum_time_evolution/imaginary/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/imaginary/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/__init__.py b/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/numpy_qite.py b/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/numpy_qite.py new file mode 100644 index 000000000000..c6e1f1fd79ae --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/numpy_qite.py @@ -0,0 +1,16 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.imaginary.qite import Qite + + +class NumPyQite(Qite): + pass diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/var_qite.py b/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/var_qite.py new file mode 100644 index 000000000000..d62128052fc7 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/var_qite.py @@ -0,0 +1,17 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.imaginary.qite import Qite +from qiskit.algorithms.quantum_time_evolution.variational.var_qte import VarQte + + +class VarQite(Qite, VarQte): + pass diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py b/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py new file mode 100644 index 000000000000..966eb4f3cbae --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py @@ -0,0 +1,32 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from abc import abstractmethod + +from qiskit.algorithms.quantum_time_evolution.evolution_base import EvolutionBase +from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import \ + EvolutionGradientResult +from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult +from qiskit.opflow import OperatorBase, StateFn + + +class Qite(EvolutionBase): + @abstractmethod + def evolve(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn = None, + observable: OperatorBase = None, t_param=None, + hamiltonian_value_dict=None) -> EvolutionResult: + raise NotImplementedError() + + @abstractmethod + def gradient(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn, + observable: OperatorBase = None, t_param=None, + hamiltonian_value_dict=None, gradient_params=None) -> EvolutionGradientResult: + raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/real/__init__.py b/qiskit/algorithms/quantum_time_evolution/real/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/real/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/__init__.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/numpy_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/numpy_qrte.py new file mode 100644 index 000000000000..9670220a5091 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/numpy_qrte.py @@ -0,0 +1,16 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte + + +class NumPyQrte(Qrte): + pass diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/projected_vqd.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/projected_vqd.py new file mode 100644 index 000000000000..baf08aef1c6b --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/projected_vqd.py @@ -0,0 +1,16 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte + + +class ProjectedVqd(Qrte): + pass diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization_qrte.py new file mode 100644 index 000000000000..749899469f80 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization_qrte.py @@ -0,0 +1,16 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte + + +class TrotterizationQrte(Qrte): + pass diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py new file mode 100644 index 000000000000..944f9b810cb3 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py @@ -0,0 +1,17 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte +from qiskit.algorithms.quantum_time_evolution.variational.var_qte import VarQte + + +class VarQrte(Qrte, VarQte): + pass diff --git a/qiskit/algorithms/quantum_time_evolution/real/qrte.py b/qiskit/algorithms/quantum_time_evolution/real/qrte.py new file mode 100644 index 000000000000..1a57e2ad39b4 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/real/qrte.py @@ -0,0 +1,32 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from abc import abstractmethod + +from qiskit.algorithms.quantum_time_evolution.evolution_base import EvolutionBase +from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import \ + EvolutionGradientResult +from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult +from qiskit.opflow import StateFn, OperatorBase + + +class Qrte(EvolutionBase): + @abstractmethod + def evolve(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn = None, + observable: OperatorBase = None, t_param=None, + hamiltonian_value_dict=None) -> EvolutionResult: + raise NotImplementedError() + + @abstractmethod + def gradient(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn, + observable: OperatorBase = None, t_param=None, + hamiltonian_value_dict=None, gradient_params=None) -> EvolutionGradientResult: + raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/results/__init__.py b/qiskit/algorithms/quantum_time_evolution/results/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/results/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py b/qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py new file mode 100644 index 000000000000..ee0812371f3e --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py @@ -0,0 +1,13 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +class EvolutionGradientResult: + pass \ No newline at end of file diff --git a/qiskit/algorithms/quantum_time_evolution/results/evolution_result.py b/qiskit/algorithms/quantum_time_evolution/results/evolution_result.py new file mode 100644 index 000000000000..146f5207703a --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/results/evolution_result.py @@ -0,0 +1,13 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +class EvolutionResult: + pass diff --git a/qiskit/algorithms/quantum_time_evolution/variational/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/variational/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/variational/principles/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py new file mode 100644 index 000000000000..5741df264653 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py @@ -0,0 +1,17 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.variational.principles.variational_principle import \ + VariationalPrinciple + + +class ImaginaryVariationalPrinciple(VariationalPrinciple): + pass diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/implementations/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/implementations/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/implementations/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/real/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/real/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/variational/principles/real/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/real/implementations/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/real/implementations/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/variational/principles/real/implementations/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py new file mode 100644 index 000000000000..e613c0a04374 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py @@ -0,0 +1,17 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.variational.principles.variational_principle import \ + VariationalPrinciple + + +class RealVariationalPrinciple(VariationalPrinciple): + pass diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/variational_principle.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/variational_principle.py new file mode 100644 index 000000000000..56f32cf0a503 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/variational/principles/variational_principle.py @@ -0,0 +1,16 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from abc import ABC + + +class VariationalPrinciple(ABC): + pass diff --git a/qiskit/algorithms/quantum_time_evolution/variational/var_qte.py b/qiskit/algorithms/quantum_time_evolution/variational/var_qte.py new file mode 100644 index 000000000000..a89947ed09a7 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/variational/var_qte.py @@ -0,0 +1,16 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from abc import ABC + + +class VarQte(ABC): + pass diff --git a/test/python/algorithms/quantum_time_evolution/__init__.py b/test/python/algorithms/quantum_time_evolution/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/test/python/algorithms/quantum_time_evolution/builders/__init__.py b/test/python/algorithms/quantum_time_evolution/builders/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/builders/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/__init__.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. From 6d36cd05a731e1beceab6815d13a19864659aa29 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 12 Jul 2021 15:58:13 +0200 Subject: [PATCH 002/145] Migrated trotterization as trotterization builder. --- .../implementations/trotterization_builder.py | 198 ++++++++++++++++++ .../implementations/trotterizations/qdrift.py | 80 +++++++ .../implementations/trotterizations/suzuki.py | 117 +++++++++++ .../trotterizations/trotter.py | 28 +++ .../trotterizations/trotter_mode_enum.py | 18 ++ .../trotterizations/trotterization_base.py | 59 ++++++ .../trotterizations/trotterization_factory.py | 43 ++++ .../test_trotterization_builder.py | 179 ++++++++++++++++ .../trotterizations/test_qdrift.py | 72 +++++++ .../trotterizations/test_suzuki.py | 43 ++++ .../trotterizations/test_trotter.py | 36 ++++ .../test_trotterization_factory.py | 11 + 12 files changed, 884 insertions(+) create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter_mode_enum.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py create mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py create mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py create mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py create mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py create mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py create mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotterization_factory.py diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py new file mode 100644 index 000000000000..ea5f5edb57da --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py @@ -0,0 +1,198 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import \ + EvolutionOpBuilder + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# 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. + +""" PauliTrotterEvolution Class """ + +import logging +from typing import Optional, Union, cast + +import numpy as np + +from qiskit.opflow.converters.pauli_basis_change import PauliBasisChange +from qiskit.opflow.evolutions.evolved_op import EvolvedOp +from qiskit.opflow.evolutions.trotterizations.trotterization_base import TrotterizationBase +from qiskit.opflow.evolutions.trotterizations.trotterization_factory import TrotterizationFactory +from qiskit.opflow.list_ops.list_op import ListOp +from qiskit.opflow.list_ops.summed_op import SummedOp +from qiskit.opflow.operator_base import OperatorBase +from qiskit.opflow.operator_globals import I, Z +from qiskit.opflow.primitive_ops.pauli_op import PauliOp +from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp +from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp + +# TODO uncomment when we implement Abelian grouped evolution. +# from qiskit.opflow.converters.abelian_grouper import AbelianGrouper + +logger = logging.getLogger(__name__) + + +class TrotterizationBuilder(EvolutionOpBuilder): + r""" + An Evolution algorithm replacing exponentiated sums of Paulis by changing them each to the + Z basis, rotating with an rZ, changing back, and Trotterizing. + + More specifically, we compute basis change circuits for each Pauli into a single-qubit Z, + evolve the Z by the desired evolution time with an rZ gate, and change the basis back using + the adjoint of the original basis change circuit. For sums of Paulis, the individual Pauli + evolution circuits are composed together by Trotterization scheme. + """ + + def __init__( + self, + trotter_mode: Optional[Union[str, TrotterizationBase]] = "trotter", + reps: Optional[int] = 1, + # TODO uncomment when we implement Abelian grouped evolution. + # group_paulis: Optional[bool] = False + ) -> None: + """ + Args: + trotter_mode: A string ('trotter', 'suzuki', or 'qdrift') to pass to the + TrotterizationFactory, or a TrotterizationBase, indicating how to combine + individual Pauli evolution circuits to equal the exponentiation of the Pauli sum. + reps: How many Trotterization repetitions to make, to improve the approximation + accuracy. + # TODO uncomment when we implement Abelian grouped evolution. + # group_paulis: Whether to group Pauli sums into Abelian + # sub-groups, so a single diagonalization circuit can be used for each group + # rather than each Pauli. + """ + + if isinstance(trotter_mode, TrotterizationBase): + self._trotter = trotter_mode + else: + self._trotter = TrotterizationFactory.build(mode=trotter_mode, reps=reps) + + # TODO uncomment when we implement Abelian grouped evolution. + # self._grouper = AbelianGrouper() if group_paulis else None + + @property + def trotter(self) -> TrotterizationBase: + """TrotterizationBase used to evolve SummedOps.""" + return self._trotter + + @trotter.setter + def trotter(self, trotter: TrotterizationBase) -> None: + """Set TrotterizationBase used to evolve SummedOps.""" + self._trotter = trotter + + def build(self, operator: OperatorBase) -> OperatorBase: + r""" + Traverse the operator, replacing ``EvolvedOps`` with ``CircuitOps`` containing + Trotterized evolutions equalling the exponentiation of -i * operator. + + Args: + operator: The Operator to be trotterized. + + Returns: + The trotterized operator. + """ + # TODO uncomment when we implement Abelian grouped evolution. + # if self._grouper: + # # Sort into commuting groups + # operator = self._grouper.convert(operator).reduce() + return self._recursive_convert(operator) + + def _recursive_convert(self, operator: OperatorBase) -> OperatorBase: + if isinstance(operator, EvolvedOp): + if isinstance(operator.primitive, PauliSumOp): + operator = EvolvedOp(operator.primitive.to_pauli_op(), coeff=operator.coeff) + if not {"Pauli"} == operator.primitive_strings(): + logger.warning( + "Evolved Hamiltonian is not composed of only Paulis, converting to " + "Pauli representation, which can be expensive." + ) + # Setting massive=False because this conversion is implicit. User can perform this + # action on the Hamiltonian with massive=True explicitly if they so choose. + # TODO explore performance to see whether we should avoid doing this repeatedly + pauli_ham = operator.primitive.to_pauli_op(massive=False) + operator = EvolvedOp(pauli_ham, coeff=operator.coeff) + + if isinstance(operator.primitive, SummedOp): + # TODO uncomment when we implement Abelian grouped evolution. + # if operator.primitive.abelian: + # return self.evolution_for_abelian_paulisum(operator.primitive) + # else: + # Collect terms that are not the identity. + oplist = [ + x + for x in operator.primitive + if not isinstance(x, PauliOp) or sum(x.primitive.x + x.primitive.z) != 0 + ] + # Collect the coefficients of any identity terms, + # which become global phases when exponentiated. + identity_phases = [ + x.coeff + for x in operator.primitive + if isinstance(x, PauliOp) and sum(x.primitive.x + x.primitive.z) == 0 + ] + # Construct sum without the identity operators. + new_primitive = SummedOp(oplist, coeff=operator.primitive.coeff) + trotterized = self.trotter.convert(new_primitive) + circuit_no_identities = self._recursive_convert(trotterized) + # Set the global phase of the QuantumCircuit to account for removed identity terms. + global_phase = -sum(identity_phases) * operator.primitive.coeff + circuit_no_identities.primitive.global_phase = global_phase + return circuit_no_identities + elif isinstance(operator.primitive, PauliOp): + return self.evolution_for_pauli(operator.primitive) + # Covers ListOp, ComposedOp, TensoredOp + elif isinstance(operator.primitive, ListOp): + converted_ops = [self._recursive_convert(op) for op in operator.primitive.oplist] + return operator.primitive.__class__(converted_ops, coeff=operator.coeff) + elif isinstance(operator, ListOp): + return operator.traverse(self.build).reduce() + + return operator + + def evolution_for_pauli(self, pauli_op: PauliOp) -> PrimitiveOp: + r""" + Compute evolution Operator for a single Pauli using a ``PauliBasisChange``. + + Args: + pauli_op: The ``PauliOp`` to evolve. + + Returns: + A ``PrimitiveOp``, either the evolution ``CircuitOp`` or a ``PauliOp`` equal to the + identity if pauli_op is the identity. + """ + + def replacement_fn(cob_instr_op, dest_pauli_op): + z_evolution = dest_pauli_op.exp_i() + # Remember, circuit composition order is mirrored operator composition order. + return cob_instr_op.adjoint().compose(z_evolution).compose(cob_instr_op) + + # Note: PauliBasisChange will pad destination with identities + # to produce correct CoB circuit + sig_bits = np.logical_or(pauli_op.primitive.z, pauli_op.primitive.x) + a_sig_bit = int(max(np.extract(sig_bits, np.arange(pauli_op.num_qubits)[::-1]))) + destination = (I.tensorpower(a_sig_bit)) ^ (Z * pauli_op.coeff) + cob = PauliBasisChange(destination_basis=destination, replacement_fn=replacement_fn) + return cast(PrimitiveOp, cob.convert(pauli_op)) + + # TODO implement Abelian grouped evolution. + def evolution_for_abelian_paulisum(self, op_sum: SummedOp) -> PrimitiveOp: + """Evolution for abelian pauli sum""" + raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py new file mode 100644 index 000000000000..8afce08f82ac --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py @@ -0,0 +1,80 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +""" +QDrift Class + +""" + +from typing import List, Union, cast + +import numpy as np + +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ + .trotterization_base import \ + TrotterizationBase +from qiskit.opflow.list_ops.composed_op import ComposedOp +from qiskit.opflow.list_ops.summed_op import SummedOp +from qiskit.opflow.operator_base import OperatorBase +from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp +from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp +from qiskit.utils import algorithm_globals + +# pylint: disable=invalid-name + + +class QDrift(TrotterizationBase): + """The QDrift Trotterization method, which selects each each term in the + Trotterization randomly, with a probability proportional to its weight. Based on the work + of Earl Campbell in https://arxiv.org/abs/1811.08017. + """ + + def __init__(self, reps: int = 1) -> None: + r""" + Args: + reps: The number of times to repeat the Trotterization circuit. + """ + super().__init__(reps=reps) + + def build(self, operator: OperatorBase) -> OperatorBase: + if not isinstance(operator, (SummedOp, PauliSumOp)): + raise TypeError("Trotterization converters can only convert SummedOp or PauliSumOp.") + + if not isinstance(operator.coeff, (float, int)): + raise TypeError( + "Trotterization converters can only convert operators with real coefficients." + ) + + operator_iter: Union[PauliSumOp, List[PrimitiveOp]] + + if isinstance(operator, PauliSumOp): + operator_iter = operator + coeffs = operator.primitive.coeffs + coeff = operator.coeff + else: + operator_iter = cast(List[PrimitiveOp], operator.oplist) + coeffs = [op.coeff for op in operator_iter] + coeff = operator.coeff + + # We artificially make the weights positive, TODO check approximation performance + weights = np.abs(coeffs) + lambd = np.sum(weights) + + N = 2 * (lambd ** 2) * (coeff ** 2) + factor = lambd * coeff / (N * self.reps) + # The protocol calls for the removal of the individual coefficients, + # and multiplication by a constant factor. + scaled_ops = [(op * (factor / op.coeff)).exp_i() for op in operator_iter] + sampled_ops = algorithm_globals.random.choice( + scaled_ops, size=(int(N * self.reps),), p=weights / lambd + ) + + return ComposedOp(sampled_ops).reduce() \ No newline at end of file diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py new file mode 100644 index 000000000000..6d648660e707 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py @@ -0,0 +1,117 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +""" Suzuki Class """ + +from typing import List, Union, cast + +from numpy import isreal + +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ + .trotterization_base import \ + TrotterizationBase +from qiskit.circuit import ParameterExpression +from qiskit.opflow.list_ops.composed_op import ComposedOp +from qiskit.opflow.list_ops.summed_op import SummedOp +from qiskit.opflow.operator_base import OperatorBase +from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp +from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp + + +class Suzuki(TrotterizationBase): + r""" + Suzuki Trotter expansion, composing the evolution circuits of each Operator in the sum + together by a recursive "bookends" strategy, repeating the whole composed circuit + ``reps`` times. + + Detailed in https://arxiv.org/pdf/quant-ph/0508139.pdf. + """ + + def __init__(self, reps: int = 1, order: int = 2) -> None: + """ + Args: + reps: The number of times to repeat the expansion circuit. + order: The order of the expansion to perform. + + """ + super().__init__(reps=reps) + self._order = order + + @property + def order(self) -> int: + """returns order""" + return self._order + + @order.setter + def order(self, order: int) -> None: + """sets order""" + self._order = order + + def build(self, operator: OperatorBase) -> OperatorBase: + if not isinstance(operator, (SummedOp, PauliSumOp)): + raise TypeError("Trotterization converters can only convert SummedOp or PauliSumOp.") + + if isinstance(operator.coeff, (float, ParameterExpression)): + coeff = operator.coeff + else: + if isreal(operator.coeff): + coeff = operator.coeff.real + else: + raise TypeError( + "Coefficient of the operator must be float or ParameterExpression, " + f"but {operator.coeff}:{type(operator.coeff)} is given." + ) + + if isinstance(operator, PauliSumOp): + comp_list = self._recursive_expansion(operator, coeff, self.order, self.reps) + if isinstance(operator, SummedOp): + comp_list = Suzuki._recursive_expansion(operator.oplist, coeff, self.order, self.reps) + + single_rep = ComposedOp(cast(List[OperatorBase], comp_list)) + full_evo = single_rep.power(self.reps) + return full_evo.reduce() + + @staticmethod + def _recursive_expansion( + op_list: Union[List[OperatorBase], PauliSumOp], + evo_time: Union[float, ParameterExpression], + expansion_order: int, + reps: int, + ) -> List[PrimitiveOp]: + """ + Compute the list of pauli terms for a single slice of the Suzuki expansion + following the paper https://arxiv.org/pdf/quant-ph/0508139.pdf. + + Args: + op_list: The slice's weighted Pauli list for the Suzuki expansion + evo_time: The parameter lambda as defined in said paper, + adjusted for the evolution time and the number of time slices + expansion_order: The order for the Suzuki expansion. + reps: The number of times to repeat the expansion circuit. + + Returns: + The evolution list after expansion. + """ + if expansion_order == 1: + # Base first-order Trotter case + return [(op * (evo_time / reps)).exp_i() for op in op_list] # type: ignore + if expansion_order == 2: + half = Suzuki._recursive_expansion(op_list, evo_time / 2, expansion_order - 1, reps) + return list(reversed(half)) + half + else: + p_k = (4 - 4 ** (1 / (2 * expansion_order - 1))) ** -1 + side = 2 * Suzuki._recursive_expansion( + op_list, evo_time * p_k, expansion_order - 2, reps + ) + middle = Suzuki._recursive_expansion( + op_list, evo_time * (1 - 4 * p_k), expansion_order - 2, reps + ) + return side + middle + side \ No newline at end of file diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py new file mode 100644 index 000000000000..4e81a85c7ed2 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py @@ -0,0 +1,28 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +""" Trotter Class """ + +from qiskit.opflow.evolutions.trotterizations.suzuki import Suzuki + + +class Trotter(Suzuki): + r""" + Simple Trotter expansion, composing the evolution circuits of each Operator in the sum + together ``reps`` times and dividing the evolution time of each by ``reps``. + """ + + def __init__(self, reps: int = 1) -> None: + r""" + Args: + reps: The number of times to repeat the Trotterization circuit. + """ + super().__init__(order=1, reps=reps) \ No newline at end of file diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter_mode_enum.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter_mode_enum.py new file mode 100644 index 000000000000..b6b80ca62ca6 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter_mode_enum.py @@ -0,0 +1,18 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from enum import Enum + + +class TrotterModeEnum(Enum): + TROTTER = "trotter" + SUZUKI = "suzuki" + QDRIFT = "qdrift" diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py new file mode 100644 index 000000000000..6dcfc5ebfaec --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py @@ -0,0 +1,59 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. + +""" Trotterization Algorithm Base """ + +from abc import abstractmethod + +from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import \ + EvolutionOpBuilder +from qiskit.opflow.operator_base import OperatorBase + +# TODO centralize handling of commuting groups + + +class TrotterizationBase(EvolutionOpBuilder): + """A base for Trotterization methods, algorithms for approximating exponentiations of + operator sums by compositions of exponentiations. + """ + + def __init__(self, reps: int = 1) -> None: + + self._reps = reps + + @property + def reps(self) -> int: + """The number of repetitions to use in the Trotterization, improving the approximation + accuracy. + """ + return self._reps + + @reps.setter + def reps(self, reps: int) -> None: + r"""Set the number of repetitions to use in the Trotterization.""" + self._reps = reps + + @abstractmethod + def build(self, operator: OperatorBase) -> OperatorBase: + r""" + Convert a ``SummedOp`` into a ``ComposedOp`` or ``CircuitOp`` representing an + approximation of e^-i*``op_sum``. + Args: + operator: The ``SummedOp`` to evolve. + Returns: + The Operator approximating op_sum's evolution. + Raises: + TypeError: A non-SummedOps Operator is passed into ``convert``. + """ + raise NotImplementedError + + # TODO @abstractmethod - trotter_error_bound diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py new file mode 100644 index 000000000000..7182d77636d7 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py @@ -0,0 +1,43 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +"""TrotterizationFactory Class """ + +from qiskit.opflow.evolutions.trotterizations.qdrift import QDrift +from qiskit.opflow.evolutions.trotterizations.suzuki import Suzuki +from qiskit.opflow.evolutions.trotterizations.trotter import Trotter +from qiskit.opflow.evolutions.trotterizations.trotterization_base import TrotterizationBase + + +class TrotterizationFactory: + """A factory for conveniently creating TrotterizationBase instances.""" + + @staticmethod + def build(mode: str = "trotter", reps: int = 1) -> TrotterizationBase: + """A factory for conveniently creating TrotterizationBase instances. + Args: + mode: One of 'trotter', 'suzuki', 'qdrift' + reps: The number of times to repeat the Trotterization circuit. + Returns: + The desired TrotterizationBase instance. + Raises: + ValueError: A string not in ['trotter', 'suzuki', 'qdrift'] is given for mode. + """ + if mode == "trotter": + return Trotter(reps=reps) + + elif mode == "suzuki": + return Suzuki(reps=reps) + + elif mode == "qdrift": + return QDrift(reps=reps) + + raise ValueError(f"Trotter mode {mode} not supported") \ No newline at end of file diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py new file mode 100644 index 000000000000..33dbdec1ad2c --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py @@ -0,0 +1,179 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. + +""" Test Trotterization Evolution Builder. """ +import unittest + +import numpy as np +import scipy.linalg + +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterization_builder \ + import \ + TrotterizationBuilder +from test.python.opflow import QiskitOpflowTestCase +import qiskit +from qiskit.circuit import ParameterVector +from qiskit.opflow import ( + CX, + EvolvedOp, + H, + I, + ListOp, + X, + Y, + Z, + Zero, +) + + +class TestTrotterizationBuilder(QiskitOpflowTestCase): + """Trotterization Evolution Builder tests.""" + + def test_trotter_with_identity(self): + """trotterization of operator with identity term""" + op = (2.0 * I ^ I) + (Z ^ Y) + exact_matrix = scipy.linalg.expm(-1j * op.to_matrix()) + evo = TrotterizationBuilder(trotter_mode="suzuki", reps=2) + with self.subTest("all PauliOp terms"): + circ_op = evo.build(EvolvedOp(op)) + circuit_matrix = qiskit.quantum_info.Operator(circ_op.to_circuit()).data + np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) + + with self.subTest("MatrixOp identity term"): + op = (2.0 * I ^ I).to_matrix_op() + (Z ^ Y) + circ_op = evo.build(EvolvedOp(op)) + circuit_matrix = qiskit.quantum_info.Operator(circ_op.to_circuit()).data + np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) + + with self.subTest("CircuitOp identity term"): + op = (2.0 * I ^ I).to_circuit_op() + (Z ^ Y) + circ_op = evo.build(EvolvedOp(op)) + circuit_matrix = qiskit.quantum_info.Operator(circ_op.to_circuit()).data + np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) + + def test_parameterized_evolution(self): + """parameterized evolution test""" + thetas = ParameterVector("θ", length=7) + op = ( + (thetas[0] * I ^ I) + + (thetas[1] * I ^ Z) + + (thetas[2] * X ^ X) + + (thetas[3] * Z ^ I) + + (thetas[4] * Y ^ Z) + + (thetas[5] * Z ^ Z) + ) + op = op * thetas[6] + evolution = TrotterizationBuilder(trotter_mode="trotter", reps=1) + # wf = (Pl^Pl) + (Ze^Ze) + wf = (op).exp_i() @ CX @ (H ^ I) @ Zero + mean = evolution.build(wf) + circuit = mean.to_circuit() + # Check that all parameters are in the circuit + for p in thetas: + self.assertIn(p, circuit.parameters) + # Check that the identity-parameters only exist as global phase + self.assertNotIn(thetas[0], circuit._parameter_table.get_keys()) + + def test_bind_parameters(self): + """bind parameters test""" + thetas = ParameterVector("θ", length=6) + op = ( + (thetas[1] * I ^ Z) + + (thetas[2] * X ^ X) + + (thetas[3] * Z ^ I) + + (thetas[4] * Y ^ Z) + + (thetas[5] * Z ^ Z) + ) + op = thetas[0] * op + evolution = TrotterizationBuilder(trotter_mode="trotter", reps=1) + # wf = (Pl^Pl) + (Ze^Ze) + wf = (op).exp_i() @ CX @ (H ^ I) @ Zero + wf = wf.assign_parameters({thetas: np.arange(10, 16)}) + mean = evolution.build(wf) + circuit_params = mean.to_circuit().parameters + # Check that the no parameters are in the circuit + for p in thetas[1:]: + self.assertNotIn(p, circuit_params) + + def test_bind_circuit_parameters(self): + """bind circuit parameters test""" + thetas = ParameterVector("θ", length=6) + op = ( + (thetas[1] * I ^ Z) + + (thetas[2] * X ^ X) + + (thetas[3] * Z ^ I) + + (thetas[4] * Y ^ Z) + + (thetas[5] * Z ^ Z) + ) + op = thetas[0] * op + evolution = TrotterizationBuilder(trotter_mode="trotter", reps=1) + # wf = (Pl^Pl) + (Ze^Ze) + wf = (op).exp_i() @ CX @ (H ^ I) @ Zero + evo = evolution.build(wf) + mean = evo.assign_parameters({thetas: np.arange(10, 16)}) + # Check that the no parameters are in the circuit + for p in thetas[1:]: + self.assertNotIn(p, mean.to_circuit().parameters) + # Check that original circuit is unchanged + for p in thetas: + self.assertIn(p, evo.to_circuit().parameters) + + # TODO test with other Op types than CircuitStateFn + def test_bind_parameter_list(self): + """bind parameters list test""" + thetas = ParameterVector("θ", length=6) + op = ( + (thetas[1] * I ^ Z) + + (thetas[2] * X ^ X) + + (thetas[3] * Z ^ I) + + (thetas[4] * Y ^ Z) + + (thetas[5] * Z ^ Z) + ) + op = thetas[0] * op + evolution = TrotterizationBuilder(trotter_mode="trotter", reps=1) + # wf = (Pl^Pl) + (Ze^Ze) + wf = (op).exp_i() @ CX @ (H ^ I) @ Zero + evo = evolution.build(wf) + param_list = np.transpose([np.arange(10, 16), np.arange(2, 8), np.arange(30, 36)]).tolist() + means = evo.assign_parameters({thetas: param_list}) + self.assertIsInstance(means, ListOp) + # Check that the no parameters are in the circuit + for p in thetas[1:]: + for circop in means.oplist: + self.assertNotIn(p, circop.to_circuit().parameters) + # Check that original circuit is unchanged + for p in thetas: + self.assertIn(p, evo.to_circuit().parameters) + + def test_mixed_evolution(self): + """bind parameters test""" + thetas = ParameterVector("θ", length=6) + op = ( + (thetas[1] * (I ^ Z).to_matrix_op()) + + (thetas[2] * (X ^ X)).to_matrix_op() + + (thetas[3] * Z ^ I) + + (thetas[4] * Y ^ Z).to_circuit_op() + + (thetas[5] * (Z ^ I).to_circuit_op()) + ) + op = thetas[0] * op + evolution = TrotterizationBuilder(trotter_mode="trotter", reps=1) + wf = (op).exp_i() @ CX @ (H ^ I) @ Zero + wf = wf.assign_parameters({thetas: np.arange(10, 16)}) + mean = evolution.build(wf) + circuit_params = mean.to_circuit().parameters + # Check that the no parameters are in the circuit + for p in thetas[1:]: + self.assertNotIn(p, circuit_params) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py new file mode 100644 index 000000000000..dbe155e7f93e --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py @@ -0,0 +1,72 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +""" Test QDrift. """ +import unittest + +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.qdrift \ + import \ + QDrift +from test.python.opflow import QiskitOpflowTestCase +from qiskit.opflow import ( + CircuitOp, + EvolvedOp, + I, + SummedOp, + X, + Y, + Z, +) + + +class TestQDrift(QiskitOpflowTestCase): + """QDrift tests.""" + + def test_qdrift(self): + """QDrift test.""" + op = (2 * Z ^ Z) + (3 * X ^ X) - (4 * Y ^ Y) + (0.5 * Z ^ I) + trotterization = QDrift().build(op) + self.assertGreater(len(trotterization.oplist), 150) + last_coeff = None + # Check that all types are correct and all coefficients are equals + for op in trotterization.oplist: + self.assertIsInstance(op, (EvolvedOp, CircuitOp)) + if isinstance(op, EvolvedOp): + if last_coeff: + self.assertEqual(op.primitive.coeff, last_coeff) + else: + last_coeff = op.primitive.coeff + + def test_qdrift_summed_op(self): + """QDrift test for SummedOp.""" + op = SummedOp( + [ + (2 * Z ^ Z), + (3 * X ^ X), + (-4 * Y ^ Y), + (0.5 * Z ^ I), + ] + ) + trotterization = QDrift().build(op) + self.assertGreater(len(trotterization.oplist), 150) + last_coeff = None + # Check that all types are correct and all coefficients are equals + for op in trotterization.oplist: + self.assertIsInstance(op, (EvolvedOp, CircuitOp)) + if isinstance(op, EvolvedOp): + if last_coeff: + self.assertEqual(op.primitive.coeff, last_coeff) + else: + last_coeff = op.primitive.coeff + + +if __name__ == "__main__": + unittest.main() diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py new file mode 100644 index 000000000000..5a2669ac43e0 --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py @@ -0,0 +1,43 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +""" Test Suzuki. """ +import unittest + +import numpy as np + +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ + import \ + Suzuki +from test.python.opflow import QiskitOpflowTestCase +from qiskit.opflow import ( + X, + Z, +) + + +class TestSuzuki(QiskitOpflowTestCase): + """Suzuki tests.""" + def test_suzuki_directly(self): + """Test for Suzuki converter""" + operator = X + Z + + evo = Suzuki() + evolution = evo.build(operator) + + matrix = np.array( + [[0.29192658 - 0.45464871j, -0.84147098j], [-0.84147098j, 0.29192658 + 0.45464871j]] + ) + np.testing.assert_array_almost_equal(evolution.to_matrix(), matrix) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py new file mode 100644 index 000000000000..6c550f718d1d --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py @@ -0,0 +1,36 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +""" Test Trotter. """ +import unittest + +import numpy as np + +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterization_builder \ + import \ + TrotterizationBuilder +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ + import \ + Suzuki +from qiskit.circuit import ParameterVector +from test.python.opflow import QiskitOpflowTestCase +from qiskit.opflow import ( + X, + Z, I, Y, CX, Zero, H, +) + + +class TestSuzuki(QiskitOpflowTestCase): + """Trotter tests.""" + + +if __name__ == "__main__": + unittest.main() diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotterization_factory.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotterization_factory.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotterization_factory.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. From 9f08963a1c882087132ae2e7d8aa46c36fafc31a Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 13 Jul 2021 10:31:59 +0200 Subject: [PATCH 003/145] Implemented enum for trotterization mode; fixed imports. --- .../implementations/trotterization_builder.py | 15 +++++++--- .../trotterizations/trotter.py | 5 ++-- .../trotterizations/trotterization_factory.py | 30 ++++++++++++------- .../test_trotterization_builder.py | 22 ++++++++------ 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py index ea5f5edb57da..240aab4ce7fe 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py @@ -23,6 +23,15 @@ # 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. +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ + .trotter_mode_enum import \ + TrotterModeEnum +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ + .trotterization_base import \ + TrotterizationBase +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ + .trotterization_factory import \ + TrotterizationFactory """ PauliTrotterEvolution Class """ @@ -33,8 +42,6 @@ from qiskit.opflow.converters.pauli_basis_change import PauliBasisChange from qiskit.opflow.evolutions.evolved_op import EvolvedOp -from qiskit.opflow.evolutions.trotterizations.trotterization_base import TrotterizationBase -from qiskit.opflow.evolutions.trotterizations.trotterization_factory import TrotterizationFactory from qiskit.opflow.list_ops.list_op import ListOp from qiskit.opflow.list_ops.summed_op import SummedOp from qiskit.opflow.operator_base import OperatorBase @@ -62,7 +69,7 @@ class TrotterizationBuilder(EvolutionOpBuilder): def __init__( self, - trotter_mode: Optional[Union[str, TrotterizationBase]] = "trotter", + trotter_mode: Optional[Union[TrotterModeEnum, TrotterizationBase]] = TrotterModeEnum.TROTTER, reps: Optional[int] = 1, # TODO uncomment when we implement Abelian grouped evolution. # group_paulis: Optional[bool] = False @@ -150,7 +157,7 @@ def _recursive_convert(self, operator: OperatorBase) -> OperatorBase: ] # Construct sum without the identity operators. new_primitive = SummedOp(oplist, coeff=operator.primitive.coeff) - trotterized = self.trotter.convert(new_primitive) + trotterized = self.trotter.build(new_primitive) circuit_no_identities = self._recursive_convert(trotterized) # Set the global phase of the QuantumCircuit to account for removed identity terms. global_phase = -sum(identity_phases) * operator.primitive.coeff diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py index 4e81a85c7ed2..7f76726bde71 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py @@ -10,8 +10,9 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """ Trotter Class """ - -from qiskit.opflow.evolutions.trotterizations.suzuki import Suzuki +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ + import \ + Suzuki class Trotter(Suzuki): diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py index 7182d77636d7..721e0118d232 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py @@ -10,18 +10,28 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """TrotterizationFactory Class """ - -from qiskit.opflow.evolutions.trotterizations.qdrift import QDrift -from qiskit.opflow.evolutions.trotterizations.suzuki import Suzuki -from qiskit.opflow.evolutions.trotterizations.trotter import Trotter -from qiskit.opflow.evolutions.trotterizations.trotterization_base import TrotterizationBase +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.qdrift \ + import \ + QDrift +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ + import \ + Suzuki +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter \ + import \ + Trotter +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ + .trotter_mode_enum import \ + TrotterModeEnum +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ + .trotterization_base import \ + TrotterizationBase class TrotterizationFactory: """A factory for conveniently creating TrotterizationBase instances.""" @staticmethod - def build(mode: str = "trotter", reps: int = 1) -> TrotterizationBase: + def build(mode: TrotterModeEnum = TrotterModeEnum.TROTTER, reps: int = 1) -> TrotterizationBase: """A factory for conveniently creating TrotterizationBase instances. Args: mode: One of 'trotter', 'suzuki', 'qdrift' @@ -31,13 +41,13 @@ def build(mode: str = "trotter", reps: int = 1) -> TrotterizationBase: Raises: ValueError: A string not in ['trotter', 'suzuki', 'qdrift'] is given for mode. """ - if mode == "trotter": + if mode == TrotterModeEnum.TROTTER: return Trotter(reps=reps) - elif mode == "suzuki": + elif mode == TrotterModeEnum.SUZUKI: return Suzuki(reps=reps) - elif mode == "qdrift": + elif mode == TrotterModeEnum.QDRIFT: return QDrift(reps=reps) - raise ValueError(f"Trotter mode {mode} not supported") \ No newline at end of file + raise ValueError(f"Trotter mode {mode.value} not supported") \ No newline at end of file diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py index 33dbdec1ad2c..33600110d04a 100644 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py @@ -15,10 +15,14 @@ import numpy as np import scipy.linalg +from qiskit import quantum_info from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterization_builder \ import \ TrotterizationBuilder +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ + .trotter_mode_enum import \ + TrotterModeEnum from test.python.opflow import QiskitOpflowTestCase import qiskit from qiskit.circuit import ParameterVector @@ -42,22 +46,22 @@ def test_trotter_with_identity(self): """trotterization of operator with identity term""" op = (2.0 * I ^ I) + (Z ^ Y) exact_matrix = scipy.linalg.expm(-1j * op.to_matrix()) - evo = TrotterizationBuilder(trotter_mode="suzuki", reps=2) + evo = TrotterizationBuilder(trotter_mode=TrotterModeEnum.SUZUKI, reps=2) with self.subTest("all PauliOp terms"): circ_op = evo.build(EvolvedOp(op)) - circuit_matrix = qiskit.quantum_info.Operator(circ_op.to_circuit()).data + circuit_matrix = quantum_info.Operator(circ_op.to_circuit()).data np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) with self.subTest("MatrixOp identity term"): op = (2.0 * I ^ I).to_matrix_op() + (Z ^ Y) circ_op = evo.build(EvolvedOp(op)) - circuit_matrix = qiskit.quantum_info.Operator(circ_op.to_circuit()).data + circuit_matrix = quantum_info.Operator(circ_op.to_circuit()).data np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) with self.subTest("CircuitOp identity term"): op = (2.0 * I ^ I).to_circuit_op() + (Z ^ Y) circ_op = evo.build(EvolvedOp(op)) - circuit_matrix = qiskit.quantum_info.Operator(circ_op.to_circuit()).data + circuit_matrix = quantum_info.Operator(circ_op.to_circuit()).data np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) def test_parameterized_evolution(self): @@ -72,7 +76,7 @@ def test_parameterized_evolution(self): + (thetas[5] * Z ^ Z) ) op = op * thetas[6] - evolution = TrotterizationBuilder(trotter_mode="trotter", reps=1) + evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) # wf = (Pl^Pl) + (Ze^Ze) wf = (op).exp_i() @ CX @ (H ^ I) @ Zero mean = evolution.build(wf) @@ -94,7 +98,7 @@ def test_bind_parameters(self): + (thetas[5] * Z ^ Z) ) op = thetas[0] * op - evolution = TrotterizationBuilder(trotter_mode="trotter", reps=1) + evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) # wf = (Pl^Pl) + (Ze^Ze) wf = (op).exp_i() @ CX @ (H ^ I) @ Zero wf = wf.assign_parameters({thetas: np.arange(10, 16)}) @@ -115,7 +119,7 @@ def test_bind_circuit_parameters(self): + (thetas[5] * Z ^ Z) ) op = thetas[0] * op - evolution = TrotterizationBuilder(trotter_mode="trotter", reps=1) + evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) # wf = (Pl^Pl) + (Ze^Ze) wf = (op).exp_i() @ CX @ (H ^ I) @ Zero evo = evolution.build(wf) @@ -139,7 +143,7 @@ def test_bind_parameter_list(self): + (thetas[5] * Z ^ Z) ) op = thetas[0] * op - evolution = TrotterizationBuilder(trotter_mode="trotter", reps=1) + evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) # wf = (Pl^Pl) + (Ze^Ze) wf = (op).exp_i() @ CX @ (H ^ I) @ Zero evo = evolution.build(wf) @@ -165,7 +169,7 @@ def test_mixed_evolution(self): + (thetas[5] * (Z ^ I).to_circuit_op()) ) op = thetas[0] * op - evolution = TrotterizationBuilder(trotter_mode="trotter", reps=1) + evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) wf = (op).exp_i() @ CX @ (H ^ I) @ Zero wf = wf.assign_parameters({thetas: np.arange(10, 16)}) mean = evolution.build(wf) From a6aee58e57a0a50774ce7d67eb0fd1eb17137bec Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 13 Jul 2021 13:24:05 +0200 Subject: [PATCH 004/145] Implemented trotter_qrte.py with unit tests. --- .../__init__.py} | 5 - .../trotterization/trotter_qrte.py | 66 ++++++++ .../quantum_time_evolution/real/__init__.py | 11 ++ .../real/implementations/__init__.py | 11 ++ .../trotterization/__init__.py | 11 ++ .../trotterization/test_trotter_qrte.py | 144 ++++++++++++++++++ 6 files changed, 243 insertions(+), 5 deletions(-) rename qiskit/algorithms/quantum_time_evolution/real/implementations/{trotterization_qrte.py => trotterization/__init__.py} (81%) create mode 100644 qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py create mode 100644 test/python/algorithms/quantum_time_evolution/real/__init__.py create mode 100644 test/python/algorithms/quantum_time_evolution/real/implementations/__init__.py create mode 100644 test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py create mode 100644 test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py similarity index 81% rename from qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization_qrte.py rename to qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py index 749899469f80..96c0cf22bec9 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py @@ -9,8 +9,3 @@ # 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. -from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte - - -class TrotterizationQrte(Qrte): - pass diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py new file mode 100644 index 000000000000..bc16ae3ba228 --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -0,0 +1,66 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ + import \ + Suzuki +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ + .trotter_mode_enum import \ + TrotterModeEnum +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ + .trotterization_factory import \ + TrotterizationFactory +from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte +from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import \ + EvolutionGradientResult +from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult +from qiskit.circuit import Parameter +from qiskit.opflow import OperatorBase, StateFn + + +class TrotterQrte(Qrte): + + def __init__(self, mode: TrotterModeEnum): + self._mode = mode + + def evolve(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn = None, + observable: OperatorBase = None, t_param: Parameter = None, + hamiltonian_value_dict=None) -> EvolutionResult: + if t_param is not None: + if hamiltonian_value_dict is None: + raise ValueError( + "t_param provided indicating a parametrized Hamiltonian but no " + "hamiltonian_value_dict provided. Parameters need to be bindable to perform " + "evolution.") + else: + hamiltonian = hamiltonian.bind_parameters(hamiltonian_value_dict) + + trotter = TrotterizationFactory.build(self._mode) + trotterized_hamiltonian = trotter.build(time * hamiltonian) + if initial_state is None and observable is None: + raise ValueError( + "TrotterQrte requires an initial state or an observable to be evolved; None " + "provided.") + elif initial_state is not None and observable is not None: + raise ValueError( + "TrotterQrte requires an initial state or an observable to be evolved; both " + "provided.") + elif initial_state is not None: + return (trotterized_hamiltonian @ initial_state).eval() + elif observable is not None: + return ( + trotterized_hamiltonian.adjoint() @ observable @ + trotterized_hamiltonian) + + def gradient(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn, + observable: OperatorBase = None, t_param=None, + hamiltonian_value_dict=None, gradient_params=None) -> EvolutionGradientResult: + raise NotImplementedError() diff --git a/test/python/algorithms/quantum_time_evolution/real/__init__.py b/test/python/algorithms/quantum_time_evolution/real/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/real/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/__init__.py b/test/python/algorithms/quantum_time_evolution/real/implementations/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py new file mode 100644 index 000000000000..c35d654b4820 --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -0,0 +1,144 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +""" Test Trotter Qrte. """ +import unittest + +import numpy as np +from numpy.testing import assert_raises + +from qiskit import QiskitError +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ + .trotter_mode_enum import \ + TrotterModeEnum +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte \ + import \ + TrotterQrte +from qiskit.quantum_info import Statevector +from qiskit.utils import algorithm_globals +from test.python.opflow import QiskitOpflowTestCase +from qiskit.circuit import Parameter +from qiskit.opflow import ( + X, + Z, + Zero, VectorStateFn, StateFn, MatrixOp, +) + + +class TestTrotterQrte(QiskitOpflowTestCase): + """Trotter Qrte tests.""" + + def test_trotter_qrte_trotter(self): + """Test for trotter qrte.""" + operator = X + Z + mode = TrotterModeEnum.TROTTER + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + evolved_state = trotter_qrte.evolve(operator, 1, initial_state) + expected_evolved_state = VectorStateFn( + Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], + dims=(2,))) + + np.testing.assert_equal(evolved_state, expected_evolved_state) + + def test_trotter_qrte_trotter_2(self): + """Test for trotter qrte.""" + operator = X + Z + mode = TrotterModeEnum.TROTTER + trotter_qrte = TrotterQrte(mode) + initial_state = StateFn([1, 0]) + evolved_state = trotter_qrte.evolve(operator, 1, initial_state) + expected_evolved_state = VectorStateFn( + Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], + dims=(2,))) + + np.testing.assert_equal(evolved_state, expected_evolved_state) + + def test_trotter_qrte_trotter_observable(self): + """Test for trotter qrte with an observable.""" + operator = X + Z + mode = TrotterModeEnum.TROTTER + trotter_qrte = TrotterQrte(mode) + observable = X + evolved_observable = trotter_qrte.evolve(operator, 1, observable=observable) + evolved_observable = evolved_observable.to_matrix_op() + expected_evolved_observable = MatrixOp([[2.99928030e-17 - 4.67110231e-17j, + -4.16146837e-01 + 9.09297427e-01j], + [-4.16146837e-01 - 9.09297427e-01j, + 0.00000000e+00 + 0.00000000e+00j]]) + np.testing.assert_equal(evolved_observable, expected_evolved_observable) + + def test_trotter_qrte_suzuki(self): + """Test for trotter qrte with Suzuki.""" + operator = X + Z + mode = TrotterModeEnum.SUZUKI + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + evolved_state = trotter_qrte.evolve(operator, 1, initial_state) + expected_evolved_state = VectorStateFn( + Statevector([0.29192658 - 0.45464871j, 0. - 0.84147098j], + dims=(2,))) + + np.testing.assert_equal(evolved_state, expected_evolved_state) + + def test_trotter_qrte_qdrift(self): + """Test for trotter qrte with QDrift.""" + algorithm_globals.random_seed = 0 + operator = X + Z + mode = TrotterModeEnum.QDRIFT + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + evolved_state = trotter_qrte.evolve(operator, 1, initial_state) + expected_evolved_state = VectorStateFn( + Statevector([0.23071786 - 0.69436148j, -0.4646314 - 0.49874749j], + dims=(2,))) + + np.testing.assert_equal(evolved_state, expected_evolved_state) + + def test_trotter_qrte_trotter_binding(self): + """Test for trotter qrte with binding.""" + t_param = Parameter("t") + operator = X * t_param + Z + hamiltonian_value_dict = {t_param: 1} + mode = TrotterModeEnum.TROTTER + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + evolved_state = trotter_qrte.evolve(operator, 1, initial_state, t_param=t_param, + hamiltonian_value_dict=hamiltonian_value_dict) + expected_evolved_state = VectorStateFn( + Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], + dims=(2,))) + + np.testing.assert_equal(evolved_state, expected_evolved_state) + + def test_trotter_qrte_trotter_binding_missing_dict(self): + """Test for trotter qrte with binding and missing dictionary..""" + t_param = Parameter("t") + operator = X * t_param + Z + mode = TrotterModeEnum.TROTTER + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + with assert_raises(ValueError): + _ = trotter_qrte.evolve(operator, 1, initial_state, t_param=t_param) + + def test_trotter_qrte_trotter_binding_missing_param(self): + """Test for trotter qrte with binding and missing param.""" + t_param = Parameter("t") + operator = X * t_param + Z + mode = TrotterModeEnum.TROTTER + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + with assert_raises(QiskitError): + _ = trotter_qrte.evolve(operator, 1, initial_state) + + +if __name__ == "__main__": + unittest.main() From 162730d3d858cc89fb3cc5ee6c7ed07d130ff49e Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 13 Jul 2021 13:28:21 +0200 Subject: [PATCH 005/145] trotter_qrte.py repetitions added. --- .../real/implementations/trotterization/trotter_qrte.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index bc16ae3ba228..9a2af16507b0 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -28,8 +28,9 @@ class TrotterQrte(Qrte): - def __init__(self, mode: TrotterModeEnum): + def __init__(self, mode: TrotterModeEnum, reps: int = 1): self._mode = mode + self._reps = reps def evolve(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn = None, observable: OperatorBase = None, t_param: Parameter = None, @@ -43,7 +44,7 @@ def evolve(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn else: hamiltonian = hamiltonian.bind_parameters(hamiltonian_value_dict) - trotter = TrotterizationFactory.build(self._mode) + trotter = TrotterizationFactory.build(self._mode, self._reps) trotterized_hamiltonian = trotter.build(time * hamiltonian) if initial_state is None and observable is None: raise ValueError( From 81aa4f5526f2b5e2b7e4c8bc84c8149229ca0fde Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 13 Jul 2021 13:32:18 +0200 Subject: [PATCH 006/145] Code refactoring. --- .../builders/evolution_op_builder.py | 1 - .../circuit_evolution_op_builder.py | 5 +- .../exact_evolution_op_builder.py | 7 +- .../implementations/trotterization_builder.py | 35 ++++---- .../implementations/trotterizations/qdrift.py | 8 +- .../implementations/trotterizations/suzuki.py | 8 +- .../trotterizations/trotter.py | 8 +- .../trotterizations/trotterization_base.py | 5 +- .../trotterizations/trotterization_factory.py | 32 ++++---- .../quantum_time_evolution/evolution_base.py | 31 ++++--- .../quantum_time_evolution/imaginary/qite.py | 30 +++++-- .../trotterization/trotter_qrte.py | 81 ++++++++++++------- .../real/implementations/var_qrte.py | 28 ++++++- .../quantum_time_evolution/real/qrte.py | 30 +++++-- .../results/evolution_gradient_result.py | 2 +- .../imaginary_variational_principle.py | 5 +- .../real/real_variational_principle.py | 5 +- .../test_trotterization_builder.py | 64 +++++++-------- .../trotterizations/test_qdrift.py | 6 +- .../trotterizations/test_suzuki.py | 9 ++- .../trotterizations/test_trotter.py | 19 +++-- .../trotterization/test_trotter_qrte.py | 56 +++++++------ 22 files changed, 294 insertions(+), 181 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py index da92f7f3d697..5a8e965c98cd 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py @@ -15,7 +15,6 @@ class EvolutionOpBuilder(ABC): - @abstractmethod def build(self, operator: OperatorBase) -> OperatorBase: raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py index 80150de937e5..3b05cd355b73 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py @@ -9,8 +9,9 @@ # 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. -from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import \ - EvolutionOpBuilder +from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import ( + EvolutionOpBuilder, +) class CircuitEvolutionOpBuilder(EvolutionOpBuilder): diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py index 4d39717247c9..69018fa10ef6 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py @@ -9,9 +9,10 @@ # 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. -from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import \ - EvolutionOpBuilder +from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import ( + EvolutionOpBuilder, +) class ExactEvolutionOpBuilder(EvolutionOpBuilder): - pass \ No newline at end of file + pass diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py index 240aab4ce7fe..dd9068a4827f 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py @@ -9,8 +9,9 @@ # 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. -from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import \ - EvolutionOpBuilder +from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import ( + EvolutionOpBuilder, +) # This code is part of Qiskit. # @@ -23,15 +24,15 @@ # 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. -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ - .trotter_mode_enum import \ - TrotterModeEnum -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ - .trotterization_base import \ - TrotterizationBase -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ - .trotterization_factory import \ - TrotterizationFactory +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( + TrotterModeEnum, +) +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_base import ( + TrotterizationBase, +) +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_factory import ( + TrotterizationFactory, +) """ PauliTrotterEvolution Class """ @@ -68,11 +69,13 @@ class TrotterizationBuilder(EvolutionOpBuilder): """ def __init__( - self, - trotter_mode: Optional[Union[TrotterModeEnum, TrotterizationBase]] = TrotterModeEnum.TROTTER, - reps: Optional[int] = 1, - # TODO uncomment when we implement Abelian grouped evolution. - # group_paulis: Optional[bool] = False + self, + trotter_mode: Optional[ + Union[TrotterModeEnum, TrotterizationBase] + ] = TrotterModeEnum.TROTTER, + reps: Optional[int] = 1, + # TODO uncomment when we implement Abelian grouped evolution. + # group_paulis: Optional[bool] = False ) -> None: """ Args: diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py index 8afce08f82ac..a73e8ccd8ab0 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py @@ -18,9 +18,9 @@ import numpy as np -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ - .trotterization_base import \ - TrotterizationBase +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_base import ( + TrotterizationBase, +) from qiskit.opflow.list_ops.composed_op import ComposedOp from qiskit.opflow.list_ops.summed_op import SummedOp from qiskit.opflow.operator_base import OperatorBase @@ -77,4 +77,4 @@ def build(self, operator: OperatorBase) -> OperatorBase: scaled_ops, size=(int(N * self.reps),), p=weights / lambd ) - return ComposedOp(sampled_ops).reduce() \ No newline at end of file + return ComposedOp(sampled_ops).reduce() diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py index 6d648660e707..6da359ad7970 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py @@ -15,9 +15,9 @@ from numpy import isreal -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ - .trotterization_base import \ - TrotterizationBase +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_base import ( + TrotterizationBase, +) from qiskit.circuit import ParameterExpression from qiskit.opflow.list_ops.composed_op import ComposedOp from qiskit.opflow.list_ops.summed_op import SummedOp @@ -114,4 +114,4 @@ def _recursive_expansion( middle = Suzuki._recursive_expansion( op_list, evo_time * (1 - 4 * p_k), expansion_order - 2, reps ) - return side + middle + side \ No newline at end of file + return side + middle + side diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py index 7f76726bde71..b4f4331adc05 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py @@ -10,9 +10,9 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """ Trotter Class """ -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ - import \ - Suzuki +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki import ( + Suzuki, +) class Trotter(Suzuki): @@ -26,4 +26,4 @@ def __init__(self, reps: int = 1) -> None: Args: reps: The number of times to repeat the Trotterization circuit. """ - super().__init__(order=1, reps=reps) \ No newline at end of file + super().__init__(order=1, reps=reps) diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py index 6dcfc5ebfaec..43ff3c17e093 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py @@ -14,8 +14,9 @@ from abc import abstractmethod -from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import \ - EvolutionOpBuilder +from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import ( + EvolutionOpBuilder, +) from qiskit.opflow.operator_base import OperatorBase # TODO centralize handling of commuting groups diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py index 721e0118d232..1e56f38b3efb 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py @@ -10,21 +10,21 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """TrotterizationFactory Class """ -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.qdrift \ - import \ - QDrift -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ - import \ - Suzuki -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter \ - import \ - Trotter -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ - .trotter_mode_enum import \ - TrotterModeEnum -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ - .trotterization_base import \ - TrotterizationBase +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.qdrift import ( + QDrift, +) +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki import ( + Suzuki, +) +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter import ( + Trotter, +) +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( + TrotterModeEnum, +) +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_base import ( + TrotterizationBase, +) class TrotterizationFactory: @@ -50,4 +50,4 @@ def build(mode: TrotterModeEnum = TrotterModeEnum.TROTTER, reps: int = 1) -> Tro elif mode == TrotterModeEnum.QDRIFT: return QDrift(reps=reps) - raise ValueError(f"Trotter mode {mode.value} not supported") \ No newline at end of file + raise ValueError(f"Trotter mode {mode.value} not supported") diff --git a/qiskit/algorithms/quantum_time_evolution/evolution_base.py b/qiskit/algorithms/quantum_time_evolution/evolution_base.py index 9adb35b971bc..a63fad677dbf 100644 --- a/qiskit/algorithms/quantum_time_evolution/evolution_base.py +++ b/qiskit/algorithms/quantum_time_evolution/evolution_base.py @@ -11,22 +11,35 @@ # that they have been altered from the originals. from abc import ABC, abstractmethod -from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import \ - EvolutionGradientResult +from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import ( + EvolutionGradientResult, +) from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult from qiskit.opflow import OperatorBase, StateFn class EvolutionBase(ABC): - @abstractmethod - def evolve(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn = None, - observable: OperatorBase = None, t_param=None, - hamiltonian_value_dict=None) -> EvolutionResult: + def evolve( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn = None, + observable: OperatorBase = None, + t_param=None, + hamiltonian_value_dict=None, + ) -> EvolutionResult: raise NotImplementedError() @abstractmethod - def gradient(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn, - observable: OperatorBase = None, t_param=None, - hamiltonian_value_dict=None, gradient_params=None) -> EvolutionGradientResult: + def gradient( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn, + observable: OperatorBase = None, + t_param=None, + hamiltonian_value_dict=None, + gradient_params=None, + ) -> EvolutionGradientResult: raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py b/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py index 966eb4f3cbae..4811bcb42153 100644 --- a/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py +++ b/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py @@ -12,21 +12,35 @@ from abc import abstractmethod from qiskit.algorithms.quantum_time_evolution.evolution_base import EvolutionBase -from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import \ - EvolutionGradientResult +from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import ( + EvolutionGradientResult, +) from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult from qiskit.opflow import OperatorBase, StateFn class Qite(EvolutionBase): @abstractmethod - def evolve(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn = None, - observable: OperatorBase = None, t_param=None, - hamiltonian_value_dict=None) -> EvolutionResult: + def evolve( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn = None, + observable: OperatorBase = None, + t_param=None, + hamiltonian_value_dict=None, + ) -> EvolutionResult: raise NotImplementedError() @abstractmethod - def gradient(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn, - observable: OperatorBase = None, t_param=None, - hamiltonian_value_dict=None, gradient_params=None) -> EvolutionGradientResult: + def gradient( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn, + observable: OperatorBase = None, + t_param=None, + hamiltonian_value_dict=None, + gradient_params=None, + ) -> EvolutionGradientResult: raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index 9a2af16507b0..51940c539704 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -9,59 +9,82 @@ # 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. -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ - import \ - Suzuki -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ - .trotter_mode_enum import \ - TrotterModeEnum -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ - .trotterization_factory import \ - TrotterizationFactory +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki import ( + Suzuki, +) +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( + TrotterModeEnum, +) +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_factory import ( + TrotterizationFactory, +) from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte -from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import \ - EvolutionGradientResult +from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import ( + EvolutionGradientResult, +) from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult from qiskit.circuit import Parameter from qiskit.opflow import OperatorBase, StateFn class TrotterQrte(Qrte): - def __init__(self, mode: TrotterModeEnum, reps: int = 1): self._mode = mode self._reps = reps - def evolve(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn = None, - observable: OperatorBase = None, t_param: Parameter = None, - hamiltonian_value_dict=None) -> EvolutionResult: + def evolve( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn = None, + observable: OperatorBase = None, + t_param: Parameter = None, + hamiltonian_value_dict=None, + ) -> EvolutionResult: + + hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict, t_param) + + trotter = TrotterizationFactory.build(self._mode, self._reps) + trotterized_hamiltonian = trotter.build(time * hamiltonian) + self._validate_input(initial_state, observable) + + if initial_state is not None: + return (trotterized_hamiltonian @ initial_state).eval() + if observable is not None: + return trotterized_hamiltonian.adjoint() @ observable @ trotterized_hamiltonian + + def _try_binding_params(self, hamiltonian, hamiltonian_value_dict, t_param): if t_param is not None: if hamiltonian_value_dict is None: raise ValueError( "t_param provided indicating a parametrized Hamiltonian but no " "hamiltonian_value_dict provided. Parameters need to be bindable to perform " - "evolution.") + "evolution." + ) else: hamiltonian = hamiltonian.bind_parameters(hamiltonian_value_dict) + return hamiltonian - trotter = TrotterizationFactory.build(self._mode, self._reps) - trotterized_hamiltonian = trotter.build(time * hamiltonian) + def _validate_input(self, initial_state, observable): if initial_state is None and observable is None: raise ValueError( "TrotterQrte requires an initial state or an observable to be evolved; None " - "provided.") + "provided." + ) elif initial_state is not None and observable is not None: raise ValueError( "TrotterQrte requires an initial state or an observable to be evolved; both " - "provided.") - elif initial_state is not None: - return (trotterized_hamiltonian @ initial_state).eval() - elif observable is not None: - return ( - trotterized_hamiltonian.adjoint() @ observable @ - trotterized_hamiltonian) + "provided." + ) - def gradient(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn, - observable: OperatorBase = None, t_param=None, - hamiltonian_value_dict=None, gradient_params=None) -> EvolutionGradientResult: + def gradient( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn, + observable: OperatorBase = None, + t_param=None, + hamiltonian_value_dict=None, + gradient_params=None, + ) -> EvolutionGradientResult: raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py index 944f9b810cb3..eeb5d524183e 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py @@ -10,8 +10,34 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte +from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import ( + EvolutionGradientResult, +) +from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult from qiskit.algorithms.quantum_time_evolution.variational.var_qte import VarQte +from qiskit.opflow import OperatorBase, StateFn class VarQrte(Qrte, VarQte): - pass + def evolve( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn = None, + observable: OperatorBase = None, + t_param=None, + hamiltonian_value_dict=None, + ) -> EvolutionResult: + raise NotImplementedError() + + def gradient( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn, + observable: OperatorBase = None, + t_param=None, + hamiltonian_value_dict=None, + gradient_params=None, + ) -> EvolutionGradientResult: + raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/real/qrte.py b/qiskit/algorithms/quantum_time_evolution/real/qrte.py index 1a57e2ad39b4..98b9175e9a37 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/qrte.py @@ -12,21 +12,35 @@ from abc import abstractmethod from qiskit.algorithms.quantum_time_evolution.evolution_base import EvolutionBase -from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import \ - EvolutionGradientResult +from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import ( + EvolutionGradientResult, +) from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult from qiskit.opflow import StateFn, OperatorBase class Qrte(EvolutionBase): @abstractmethod - def evolve(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn = None, - observable: OperatorBase = None, t_param=None, - hamiltonian_value_dict=None) -> EvolutionResult: + def evolve( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn = None, + observable: OperatorBase = None, + t_param=None, + hamiltonian_value_dict=None, + ) -> EvolutionResult: raise NotImplementedError() @abstractmethod - def gradient(self, hamiltonian: OperatorBase, time: float, initial_state: StateFn, - observable: OperatorBase = None, t_param=None, - hamiltonian_value_dict=None, gradient_params=None) -> EvolutionGradientResult: + def gradient( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn, + observable: OperatorBase = None, + t_param=None, + hamiltonian_value_dict=None, + gradient_params=None, + ) -> EvolutionGradientResult: raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py b/qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py index ee0812371f3e..314fd52831d4 100644 --- a/qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py +++ b/qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py @@ -10,4 +10,4 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. class EvolutionGradientResult: - pass \ No newline at end of file + pass diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py index 5741df264653..c2d24e023a0c 100644 --- a/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py +++ b/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py @@ -9,8 +9,9 @@ # 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. -from qiskit.algorithms.quantum_time_evolution.variational.principles.variational_principle import \ - VariationalPrinciple +from qiskit.algorithms.quantum_time_evolution.variational.principles.variational_principle import ( + VariationalPrinciple, +) class ImaginaryVariationalPrinciple(VariationalPrinciple): diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py index e613c0a04374..96d37f7d2389 100644 --- a/qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py +++ b/qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py @@ -9,8 +9,9 @@ # 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. -from qiskit.algorithms.quantum_time_evolution.variational.principles.variational_principle import \ - VariationalPrinciple +from qiskit.algorithms.quantum_time_evolution.variational.principles.variational_principle import ( + VariationalPrinciple, +) class RealVariationalPrinciple(VariationalPrinciple): diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py index 33600110d04a..b9131a490c96 100644 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py @@ -17,12 +17,12 @@ import scipy.linalg from qiskit import quantum_info -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterization_builder \ - import \ - TrotterizationBuilder -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ - .trotter_mode_enum import \ - TrotterModeEnum +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterization_builder import ( + TrotterizationBuilder, +) +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( + TrotterModeEnum, +) from test.python.opflow import QiskitOpflowTestCase import qiskit from qiskit.circuit import ParameterVector @@ -68,12 +68,12 @@ def test_parameterized_evolution(self): """parameterized evolution test""" thetas = ParameterVector("θ", length=7) op = ( - (thetas[0] * I ^ I) - + (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) + (thetas[0] * I ^ I) + + (thetas[1] * I ^ Z) + + (thetas[2] * X ^ X) + + (thetas[3] * Z ^ I) + + (thetas[4] * Y ^ Z) + + (thetas[5] * Z ^ Z) ) op = op * thetas[6] evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) @@ -91,11 +91,11 @@ def test_bind_parameters(self): """bind parameters test""" thetas = ParameterVector("θ", length=6) op = ( - (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) + (thetas[1] * I ^ Z) + + (thetas[2] * X ^ X) + + (thetas[3] * Z ^ I) + + (thetas[4] * Y ^ Z) + + (thetas[5] * Z ^ Z) ) op = thetas[0] * op evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) @@ -112,11 +112,11 @@ def test_bind_circuit_parameters(self): """bind circuit parameters test""" thetas = ParameterVector("θ", length=6) op = ( - (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) + (thetas[1] * I ^ Z) + + (thetas[2] * X ^ X) + + (thetas[3] * Z ^ I) + + (thetas[4] * Y ^ Z) + + (thetas[5] * Z ^ Z) ) op = thetas[0] * op evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) @@ -136,11 +136,11 @@ def test_bind_parameter_list(self): """bind parameters list test""" thetas = ParameterVector("θ", length=6) op = ( - (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) + (thetas[1] * I ^ Z) + + (thetas[2] * X ^ X) + + (thetas[3] * Z ^ I) + + (thetas[4] * Y ^ Z) + + (thetas[5] * Z ^ Z) ) op = thetas[0] * op evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) @@ -162,11 +162,11 @@ def test_mixed_evolution(self): """bind parameters test""" thetas = ParameterVector("θ", length=6) op = ( - (thetas[1] * (I ^ Z).to_matrix_op()) - + (thetas[2] * (X ^ X)).to_matrix_op() - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z).to_circuit_op() - + (thetas[5] * (Z ^ I).to_circuit_op()) + (thetas[1] * (I ^ Z).to_matrix_op()) + + (thetas[2] * (X ^ X)).to_matrix_op() + + (thetas[3] * Z ^ I) + + (thetas[4] * Y ^ Z).to_circuit_op() + + (thetas[5] * (Z ^ I).to_circuit_op()) ) op = thetas[0] * op evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py index dbe155e7f93e..5adea9f03d73 100644 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py @@ -12,9 +12,9 @@ """ Test QDrift. """ import unittest -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.qdrift \ - import \ - QDrift +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.qdrift import ( + QDrift, +) from test.python.opflow import QiskitOpflowTestCase from qiskit.opflow import ( CircuitOp, diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py index 5a2669ac43e0..64d0fbc2c498 100644 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py @@ -14,9 +14,9 @@ import numpy as np -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ - import \ - Suzuki +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki import ( + Suzuki, +) from test.python.opflow import QiskitOpflowTestCase from qiskit.opflow import ( X, @@ -26,6 +26,7 @@ class TestSuzuki(QiskitOpflowTestCase): """Suzuki tests.""" + def test_suzuki_directly(self): """Test for Suzuki converter""" operator = X + Z @@ -40,4 +41,4 @@ def test_suzuki_directly(self): if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py index 6c550f718d1d..117eede978f8 100644 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py @@ -14,17 +14,22 @@ import numpy as np -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterization_builder \ - import \ - TrotterizationBuilder -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ - import \ - Suzuki +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterization_builder import ( + TrotterizationBuilder, +) +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki import ( + Suzuki, +) from qiskit.circuit import ParameterVector from test.python.opflow import QiskitOpflowTestCase from qiskit.opflow import ( X, - Z, I, Y, CX, Zero, H, + Z, + I, + Y, + CX, + Zero, + H, ) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index c35d654b4820..7d61d8e45661 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -16,12 +16,12 @@ from numpy.testing import assert_raises from qiskit import QiskitError -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ - .trotter_mode_enum import \ - TrotterModeEnum -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte \ - import \ - TrotterQrte +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( + TrotterModeEnum, +) +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( + TrotterQrte, +) from qiskit.quantum_info import Statevector from qiskit.utils import algorithm_globals from test.python.opflow import QiskitOpflowTestCase @@ -29,7 +29,10 @@ from qiskit.opflow import ( X, Z, - Zero, VectorStateFn, StateFn, MatrixOp, + Zero, + VectorStateFn, + StateFn, + MatrixOp, ) @@ -44,8 +47,8 @@ def test_trotter_qrte_trotter(self): initial_state = Zero evolved_state = trotter_qrte.evolve(operator, 1, initial_state) expected_evolved_state = VectorStateFn( - Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], - dims=(2,))) + Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], dims=(2,)) + ) np.testing.assert_equal(evolved_state, expected_evolved_state) @@ -57,8 +60,8 @@ def test_trotter_qrte_trotter_2(self): initial_state = StateFn([1, 0]) evolved_state = trotter_qrte.evolve(operator, 1, initial_state) expected_evolved_state = VectorStateFn( - Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], - dims=(2,))) + Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], dims=(2,)) + ) np.testing.assert_equal(evolved_state, expected_evolved_state) @@ -70,10 +73,12 @@ def test_trotter_qrte_trotter_observable(self): observable = X evolved_observable = trotter_qrte.evolve(operator, 1, observable=observable) evolved_observable = evolved_observable.to_matrix_op() - expected_evolved_observable = MatrixOp([[2.99928030e-17 - 4.67110231e-17j, - -4.16146837e-01 + 9.09297427e-01j], - [-4.16146837e-01 - 9.09297427e-01j, - 0.00000000e+00 + 0.00000000e+00j]]) + expected_evolved_observable = MatrixOp( + [ + [2.99928030e-17 - 4.67110231e-17j, -4.16146837e-01 + 9.09297427e-01j], + [-4.16146837e-01 - 9.09297427e-01j, 0.00000000e00 + 0.00000000e00j], + ] + ) np.testing.assert_equal(evolved_observable, expected_evolved_observable) def test_trotter_qrte_suzuki(self): @@ -84,8 +89,8 @@ def test_trotter_qrte_suzuki(self): initial_state = Zero evolved_state = trotter_qrte.evolve(operator, 1, initial_state) expected_evolved_state = VectorStateFn( - Statevector([0.29192658 - 0.45464871j, 0. - 0.84147098j], - dims=(2,))) + Statevector([0.29192658 - 0.45464871j, 0.0 - 0.84147098j], dims=(2,)) + ) np.testing.assert_equal(evolved_state, expected_evolved_state) @@ -98,8 +103,8 @@ def test_trotter_qrte_qdrift(self): initial_state = Zero evolved_state = trotter_qrte.evolve(operator, 1, initial_state) expected_evolved_state = VectorStateFn( - Statevector([0.23071786 - 0.69436148j, -0.4646314 - 0.49874749j], - dims=(2,))) + Statevector([0.23071786 - 0.69436148j, -0.4646314 - 0.49874749j], dims=(2,)) + ) np.testing.assert_equal(evolved_state, expected_evolved_state) @@ -111,11 +116,16 @@ def test_trotter_qrte_trotter_binding(self): mode = TrotterModeEnum.TROTTER trotter_qrte = TrotterQrte(mode) initial_state = Zero - evolved_state = trotter_qrte.evolve(operator, 1, initial_state, t_param=t_param, - hamiltonian_value_dict=hamiltonian_value_dict) + evolved_state = trotter_qrte.evolve( + operator, + 1, + initial_state, + t_param=t_param, + hamiltonian_value_dict=hamiltonian_value_dict, + ) expected_evolved_state = VectorStateFn( - Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], - dims=(2,))) + Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], dims=(2,)) + ) np.testing.assert_equal(evolved_state, expected_evolved_state) From 7ad3bdc8625c01d638baa18911a0e1a572a476e5 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 13 Jul 2021 17:05:37 +0200 Subject: [PATCH 007/145] Code refactoring. --- ....py => pauli_trotter_evolution_op_builder.py} | 2 +- .../test_trotterization_builder.py | 16 ++++++++-------- .../trotterizations/test_trotter.py | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) rename qiskit/algorithms/quantum_time_evolution/builders/implementations/{trotterization_builder.py => pauli_trotter_evolution_op_builder.py} (99%) diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/pauli_trotter_evolution_op_builder.py similarity index 99% rename from qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py rename to qiskit/algorithms/quantum_time_evolution/builders/implementations/pauli_trotter_evolution_op_builder.py index dd9068a4827f..7639680ba136 100644 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterization_builder.py +++ b/qiskit/algorithms/quantum_time_evolution/builders/implementations/pauli_trotter_evolution_op_builder.py @@ -57,7 +57,7 @@ logger = logging.getLogger(__name__) -class TrotterizationBuilder(EvolutionOpBuilder): +class PauliTrotterEvolutionOpBuilder(EvolutionOpBuilder): r""" An Evolution algorithm replacing exponentiated sums of Paulis by changing them each to the Z basis, rotating with an rZ, changing back, and Trotterizing. diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py index b9131a490c96..703e206f2888 100644 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py @@ -17,8 +17,8 @@ import scipy.linalg from qiskit import quantum_info -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterization_builder import ( - TrotterizationBuilder, +from qiskit.algorithms.quantum_time_evolution.builders.implementations.pauli_trotter_evolution_op_builder import ( + PauliTrotterEvolutionOpBuilder, ) from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( TrotterModeEnum, @@ -46,7 +46,7 @@ def test_trotter_with_identity(self): """trotterization of operator with identity term""" op = (2.0 * I ^ I) + (Z ^ Y) exact_matrix = scipy.linalg.expm(-1j * op.to_matrix()) - evo = TrotterizationBuilder(trotter_mode=TrotterModeEnum.SUZUKI, reps=2) + evo = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.SUZUKI, reps=2) with self.subTest("all PauliOp terms"): circ_op = evo.build(EvolvedOp(op)) circuit_matrix = quantum_info.Operator(circ_op.to_circuit()).data @@ -76,7 +76,7 @@ def test_parameterized_evolution(self): + (thetas[5] * Z ^ Z) ) op = op * thetas[6] - evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) + evolution = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) # wf = (Pl^Pl) + (Ze^Ze) wf = (op).exp_i() @ CX @ (H ^ I) @ Zero mean = evolution.build(wf) @@ -98,7 +98,7 @@ def test_bind_parameters(self): + (thetas[5] * Z ^ Z) ) op = thetas[0] * op - evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) + evolution = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) # wf = (Pl^Pl) + (Ze^Ze) wf = (op).exp_i() @ CX @ (H ^ I) @ Zero wf = wf.assign_parameters({thetas: np.arange(10, 16)}) @@ -119,7 +119,7 @@ def test_bind_circuit_parameters(self): + (thetas[5] * Z ^ Z) ) op = thetas[0] * op - evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) + evolution = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) # wf = (Pl^Pl) + (Ze^Ze) wf = (op).exp_i() @ CX @ (H ^ I) @ Zero evo = evolution.build(wf) @@ -143,7 +143,7 @@ def test_bind_parameter_list(self): + (thetas[5] * Z ^ Z) ) op = thetas[0] * op - evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) + evolution = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) # wf = (Pl^Pl) + (Ze^Ze) wf = (op).exp_i() @ CX @ (H ^ I) @ Zero evo = evolution.build(wf) @@ -169,7 +169,7 @@ def test_mixed_evolution(self): + (thetas[5] * (Z ^ I).to_circuit_op()) ) op = thetas[0] * op - evolution = TrotterizationBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) + evolution = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) wf = (op).exp_i() @ CX @ (H ^ I) @ Zero wf = wf.assign_parameters({thetas: np.arange(10, 16)}) mean = evolution.build(wf) diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py index 117eede978f8..9acc5d588e43 100644 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py +++ b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py @@ -14,8 +14,8 @@ import numpy as np -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterization_builder import ( - TrotterizationBuilder, +from qiskit.algorithms.quantum_time_evolution.builders.implementations.pauli_trotter_evolution_op_builder import ( + PauliTrotterEvolutionOpBuilder, ) from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki import ( Suzuki, From d92d49f16708f0b33b6272e1e64373ceb8386599 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 14 Jul 2021 10:48:45 +0200 Subject: [PATCH 008/145] Gradient object introduced in a signature. --- qiskit/algorithms/quantum_time_evolution/evolution_base.py | 3 ++- .../real/implementations/trotterization/trotter_qrte.py | 5 ++++- .../quantum_time_evolution/real/implementations/var_qrte.py | 3 ++- qiskit/algorithms/quantum_time_evolution/real/qrte.py | 3 ++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/evolution_base.py b/qiskit/algorithms/quantum_time_evolution/evolution_base.py index a63fad677dbf..041ea495303b 100644 --- a/qiskit/algorithms/quantum_time_evolution/evolution_base.py +++ b/qiskit/algorithms/quantum_time_evolution/evolution_base.py @@ -15,7 +15,7 @@ EvolutionGradientResult, ) from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult -from qiskit.opflow import OperatorBase, StateFn +from qiskit.opflow import OperatorBase, StateFn, Gradient class EvolutionBase(ABC): @@ -37,6 +37,7 @@ def gradient( hamiltonian: OperatorBase, time: float, initial_state: StateFn, + gradient_object: Gradient, observable: OperatorBase = None, t_param=None, hamiltonian_value_dict=None, diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index 51940c539704..9088c0764142 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -24,7 +24,7 @@ ) from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult from qiskit.circuit import Parameter -from qiskit.opflow import OperatorBase, StateFn +from qiskit.opflow import OperatorBase, StateFn, Gradient class TrotterQrte(Qrte): @@ -82,9 +82,12 @@ def gradient( hamiltonian: OperatorBase, time: float, initial_state: StateFn, + gradient_object: Gradient, observable: OperatorBase = None, t_param=None, hamiltonian_value_dict=None, gradient_params=None, ) -> EvolutionGradientResult: raise NotImplementedError() + + diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py index eeb5d524183e..2684f846f649 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py @@ -15,7 +15,7 @@ ) from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult from qiskit.algorithms.quantum_time_evolution.variational.var_qte import VarQte -from qiskit.opflow import OperatorBase, StateFn +from qiskit.opflow import OperatorBase, StateFn, Gradient class VarQrte(Qrte, VarQte): @@ -35,6 +35,7 @@ def gradient( hamiltonian: OperatorBase, time: float, initial_state: StateFn, + gradient_object: Gradient, observable: OperatorBase = None, t_param=None, hamiltonian_value_dict=None, diff --git a/qiskit/algorithms/quantum_time_evolution/real/qrte.py b/qiskit/algorithms/quantum_time_evolution/real/qrte.py index 98b9175e9a37..8ebead4e2434 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/qrte.py @@ -16,7 +16,7 @@ EvolutionGradientResult, ) from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult -from qiskit.opflow import StateFn, OperatorBase +from qiskit.opflow import StateFn, OperatorBase, Gradient class Qrte(EvolutionBase): @@ -38,6 +38,7 @@ def gradient( hamiltonian: OperatorBase, time: float, initial_state: StateFn, + gradient_object: Gradient, observable: OperatorBase = None, t_param=None, hamiltonian_value_dict=None, From ba9726cac46fe2e8be603422ccb06838330b93ec Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 23 Jul 2021 16:12:47 +0200 Subject: [PATCH 009/145] Draft of trotter_qrte.py gradient with unit tests. --- .../trotterization/trotter_qrte.py | 193 ++++++++++++++---- .../trotterization/test_trotter_qrte.py | 63 +++++- 2 files changed, 213 insertions(+), 43 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index 9088c0764142..36a1e80c7098 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -9,13 +9,19 @@ # 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. -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki import ( +from collections import defaultdict +from typing import Union, Optional + +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ + import ( Suzuki, ) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ + .trotter_mode_enum import ( TrotterModeEnum, ) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_factory import ( +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ + .trotterization_factory import ( TrotterizationFactory, ) from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte @@ -23,8 +29,8 @@ EvolutionGradientResult, ) from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult -from qiskit.circuit import Parameter -from qiskit.opflow import OperatorBase, StateFn, Gradient +from qiskit.circuit import Parameter, ParameterExpression +from qiskit.opflow import OperatorBase, StateFn, Gradient, commutator, SummedOp, PauliSumOp, PauliOp class TrotterQrte(Qrte): @@ -33,37 +39,46 @@ def __init__(self, mode: TrotterModeEnum, reps: int = 1): self._reps = reps def evolve( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn = None, - observable: OperatorBase = None, - t_param: Parameter = None, - hamiltonian_value_dict=None, + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn = None, + observable: OperatorBase = None, + t_param: Parameter = None, + hamiltonian_value_dict=None, ) -> EvolutionResult: - hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict, t_param) + hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) + self._validate_input(initial_state, observable) trotter = TrotterizationFactory.build(self._mode, self._reps) - trotterized_hamiltonian = trotter.build(time * hamiltonian) - self._validate_input(initial_state, observable) + trotterized_evolution_op = trotter.build(time * hamiltonian) if initial_state is not None: - return (trotterized_hamiltonian @ initial_state).eval() + return (trotterized_evolution_op @ initial_state).eval() if observable is not None: - return trotterized_hamiltonian.adjoint() @ observable @ trotterized_hamiltonian - - def _try_binding_params(self, hamiltonian, hamiltonian_value_dict, t_param): - if t_param is not None: - if hamiltonian_value_dict is None: - raise ValueError( - "t_param provided indicating a parametrized Hamiltonian but no " - "hamiltonian_value_dict provided. Parameters need to be bindable to perform " - "evolution." - ) - else: - hamiltonian = hamiltonian.bind_parameters(hamiltonian_value_dict) - return hamiltonian + return trotterized_evolution_op.adjoint() @ observable @ trotterized_evolution_op + + def _try_binding_params(self, hamiltonian, hamiltonian_value_dict): + # PauliSumOp does not allow parametrized coefficients + if isinstance(hamiltonian, SummedOp): + op_list = [] + for op in hamiltonian.oplist: + if hamiltonian_value_dict is not None: + op_bound = op.bind_parameters(hamiltonian_value_dict) + else: + op_bound = op + if len(op_bound.parameters) > 0: + raise ValueError( + f"Did not manage to bind all parameters in the Hamiltonian, " + f"these parameters encountered: {op_bound.parameters}.") + op_list.append(op_bound) + return SummedOp(op_list) + #for an observable, we might have an OperatorBase... TODO + elif isinstance(hamiltonian, PauliOp): + return hamiltonian.bind_parameters(hamiltonian_value_dict) + else: + return hamiltonian def _validate_input(self, initial_state, observable): if initial_state is None and observable is None: @@ -78,16 +93,116 @@ def _validate_input(self, initial_state, observable): ) def gradient( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn, - gradient_object: Gradient, - observable: OperatorBase = None, - t_param=None, - hamiltonian_value_dict=None, - gradient_params=None, + self, + hamiltonian: Union[PauliSumOp, SummedOp], + time: float, + initial_state: StateFn, + gradient_object: Optional[Gradient], + observable: OperatorBase = None, + t_param=None, + hamiltonian_value_dict=None, + gradient_params=None, ) -> EvolutionGradientResult: - raise NotImplementedError() + if observable is None: + raise NotImplementedError( + "Observable not provided. Probability gradients are not yet supported by " + "TrotterQrte. " + ) + if gradient_object is not None: + raise Warning( + "TrotterQrte does not support custom Gradient method. Provided Gradient object is " + "ignored.") + self._validate_hamiltonian_form(hamiltonian) + + if t_param in gradient_params: + epsilon = 0.01 + hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) + evolved_state1 = self.evolve(hamiltonian, time + epsilon, initial_state, t_param=t_param) + evolved_state2 = self.evolve(hamiltonian, time - epsilon, initial_state, t_param=t_param) + expected_val_1 = ~StateFn(observable) @ evolved_state1 + expected_val_2 = ~StateFn(observable) @ evolved_state2 + finite_difference = (expected_val_1 - expected_val_2) / (2 * epsilon) + return finite_difference.eval() + elif set(gradient_params) == set(hamiltonian.parameters): + gradients = defaultdict(float) + if isinstance(hamiltonian, SummedOp): + for gradient_param in gradient_params: + # the whole SummedOp might be multiplied by a parameter of interest + if gradient_param == hamiltonian.coeff: + gradient = self._calc_term_gradient(hamiltonian, hamiltonian, + initial_state, observable, t_param, + time, hamiltonian_value_dict) + gradients[gradient_param] += gradient + for hamiltonian_term in hamiltonian.oplist: + if gradient_param in hamiltonian_term.parameters: + gradient = self._calc_term_gradient(hamiltonian, hamiltonian_term, + initial_state, observable, t_param, + time, hamiltonian_value_dict) + gradients[gradient_param] += gradient + # PauliSumOp has a coefficient which is complex or a parameter. If complex, it will be + # skipped for a particular gradient as expected. + elif isinstance(hamiltonian, PauliSumOp): + for gradient_param in gradient_params: + # the whole PauliSumOp might be multiplied by a parameter of interest + if gradient_param == hamiltonian.coeff: + gradient = self._calc_term_gradient(hamiltonian, hamiltonian, + initial_state, observable, t_param, + time, hamiltonian_value_dict) + gradients[gradient_param] += gradient + for hamiltonian_term in hamiltonian: + if gradient_param in hamiltonian_term.parameters: + gradient = self._calc_term_gradient(hamiltonian, + hamiltonian_term.primitive, + initial_state, observable, t_param, + time, hamiltonian_value_dict) + gradients[gradient_param] += gradient + + return gradients + elif any(gradient_params) not in (hamiltonian.parameters or [t_param]): + raise ValueError("gradient_params provided that are not found in hamiltonian.params " + "and not a t_param.") + + def _calc_term_gradient(self, hamiltonian, + hamiltonian_term: Union[OperatorBase, PauliSumOp, SummedOp], + initial_state, observable, t_param, + time, hamiltonian_value_dict): + hamiltonian_term = self._try_binding_params(hamiltonian_term, hamiltonian_value_dict) + custom_observable = commutator(1j * time * hamiltonian_term, observable) + hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) + + evolved_state = self.evolve(hamiltonian, time, initial_state, t_param, + hamiltonian_value_dict=hamiltonian_value_dict) + + gradient = ~StateFn(custom_observable) @ evolved_state + gradient = gradient.eval() + return gradient + def _validate_hamiltonian_form(self, hamiltonian: Union[OperatorBase, PauliSumOp, SummedOp]): + if isinstance(hamiltonian, SummedOp): + for op in hamiltonian.oplist: + if not isinstance(op.coeff, ParameterExpression): + raise ValueError( + "Term of a Hamiltonian has a coefficient that is not a " + "ParameterExpression. It is not allowed.") + if len(op.coeff.parameters) > 1: + raise ValueError( + "Term of a Hamiltonian has a coefficient that depends on several " + "parameters. Only dependence on a single parameter is allowed.") + # TODO check if param linear + elif isinstance(hamiltonian, PauliSumOp): + for op in hamiltonian: + if len(op.coeffs) > 1: + raise ValueError( + "Term of a Hamiltonian has multiple coefficients. It is not allowed.") + if not isinstance(op.coeffs[0], ParameterExpression): + raise ValueError( + "Term of a Hamiltonian has a coefficient that is not a " + "ParameterExpression. It is not allowed.") + if len(op.coeffs[0].parameters) > 1: + raise ValueError( + "Term of a Hamiltonian has a coefficient that depends on several " + "parameters. Only dependence on a single parameter is allowed.") + # TODO check if param linear + else: + raise ValueError("Hamiltonian not a SummedOp or PauliSumOp") diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 7d61d8e45661..a561bb87966c 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -16,10 +16,12 @@ from numpy.testing import assert_raises from qiskit import QiskitError -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ + .trotter_mode_enum import ( TrotterModeEnum, ) -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte \ + import ( TrotterQrte, ) from qiskit.quantum_info import Statevector @@ -32,7 +34,7 @@ Zero, VectorStateFn, StateFn, - MatrixOp, + MatrixOp, I, PauliSumOp, Y, ) @@ -146,9 +148,62 @@ def test_trotter_qrte_trotter_binding_missing_param(self): mode = TrotterModeEnum.TROTTER trotter_qrte = TrotterQrte(mode) initial_state = Zero - with assert_raises(QiskitError): + with assert_raises(ValueError): _ = trotter_qrte.evolve(operator, 1, initial_state) + def test_trotter_qrte_gradient_summed_op_qdrift(self): + """Test for trotter qrte gradient with SummedOp and QDrift.""" + theta1 = Parameter('theta1') + theta2 = Parameter('theta2') + operator = theta1 * (I ^ Z ^ I) + theta2 * (Z ^ I ^ I) + mode = TrotterModeEnum.QDRIFT + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + time = 5 + gradient_object = None + observable = Z^Y^Z + value_dict = {theta1: 2, theta2: 3} + gradient = trotter_qrte.gradient(operator, time, initial_state, gradient_object, + observable, hamiltonian_value_dict=value_dict, + gradient_params=[theta1, theta2]) + + print(gradient) + + def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): + """Test for trotter qrte gradient with SummedOp and QDrift; missing param.""" + theta1 = Parameter('theta1') + operator = theta1 * (I ^ Z ^ I) + 5 * (Z ^ I ^ I) + mode = TrotterModeEnum.QDRIFT + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + time = 5 + gradient_object = None + observable = Z + value_dict = {theta1: 2} + with assert_raises(ValueError): + _ = trotter_qrte.gradient(operator, time, initial_state, gradient_object, + observable, hamiltonian_value_dict=value_dict, + gradient_params=[theta1]) + + def test_trotter_qrte_gradient_summed_op_qdrift_t_param_grad(self): + """Test for trotter qrte gradient with SummedOp and QDrift; missing param.""" + t_param = Parameter('t_param') + theta1 = Parameter('theta1') + operator = t_param * (I ^ Z ^ I) + theta1 * (Z ^ I ^ I) + mode = TrotterModeEnum.QDRIFT + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + time = 5 + gradient_object = None + observable = Z + value_dict = {theta1: 2, t_param: 5} + gradient = trotter_qrte.gradient(operator, time, initial_state, gradient_object, + observable, t_param=t_param, + hamiltonian_value_dict=value_dict, + gradient_params=[t_param]) + + print(gradient) + if __name__ == "__main__": unittest.main() From eb6935c2fcef4430b7198e8e265421f577d74823 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 26 Jul 2021 15:22:01 +0200 Subject: [PATCH 010/145] trotter_qrte.py improvements; unit tests extended. --- .../trotterization/trotter_qrte.py | 184 +++++++++--------- .../trotterization/test_trotter_qrte.py | 71 ++++--- 2 files changed, 140 insertions(+), 115 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index 36a1e80c7098..4fac6cc646b1 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -12,15 +12,11 @@ from collections import defaultdict from typing import Union, Optional -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki \ - import ( - Suzuki, -) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ .trotter_mode_enum import ( TrotterModeEnum, ) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ .trotterization_factory import ( TrotterizationFactory, ) @@ -39,13 +35,13 @@ def __init__(self, mode: TrotterModeEnum, reps: int = 1): self._reps = reps def evolve( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn = None, - observable: OperatorBase = None, - t_param: Parameter = None, - hamiltonian_value_dict=None, + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn = None, + observable: OperatorBase = None, + t_param: Parameter = None, + hamiltonian_value_dict=None, ) -> EvolutionResult: hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) @@ -71,10 +67,11 @@ def _try_binding_params(self, hamiltonian, hamiltonian_value_dict): if len(op_bound.parameters) > 0: raise ValueError( f"Did not manage to bind all parameters in the Hamiltonian, " - f"these parameters encountered: {op_bound.parameters}.") + f"these parameters encountered: {op_bound.parameters}." + ) op_list.append(op_bound) return SummedOp(op_list) - #for an observable, we might have an OperatorBase... TODO + # for an observable, we might have an OperatorBase... TODO elif isinstance(hamiltonian, PauliOp): return hamiltonian.bind_parameters(hamiltonian_value_dict) else: @@ -93,15 +90,15 @@ def _validate_input(self, initial_state, observable): ) def gradient( - self, - hamiltonian: Union[PauliSumOp, SummedOp], - time: float, - initial_state: StateFn, - gradient_object: Optional[Gradient], - observable: OperatorBase = None, - t_param=None, - hamiltonian_value_dict=None, - gradient_params=None, + self, + hamiltonian: Union[SummedOp], + time: float, + initial_state: StateFn, + gradient_object: Optional[Gradient], + observable: OperatorBase = None, + t_param=None, + hamiltonian_value_dict=None, + gradient_params=None, ) -> EvolutionGradientResult: if observable is None: raise NotImplementedError( @@ -111,98 +108,105 @@ def gradient( if gradient_object is not None: raise Warning( "TrotterQrte does not support custom Gradient method. Provided Gradient object is " - "ignored.") + "ignored." + ) + self._validate_hamiltonian_form(hamiltonian) if t_param in gradient_params: - epsilon = 0.01 - hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) - evolved_state1 = self.evolve(hamiltonian, time + epsilon, initial_state, t_param=t_param) - evolved_state2 = self.evolve(hamiltonian, time - epsilon, initial_state, t_param=t_param) - expected_val_1 = ~StateFn(observable) @ evolved_state1 - expected_val_2 = ~StateFn(observable) @ evolved_state2 - finite_difference = (expected_val_1 - expected_val_2) / (2 * epsilon) + finite_difference = self._calc_time_gradient_finite_diff( + hamiltonian, hamiltonian_value_dict, initial_state, observable, t_param, time + ) return finite_difference.eval() elif set(gradient_params) == set(hamiltonian.parameters): gradients = defaultdict(float) if isinstance(hamiltonian, SummedOp): for gradient_param in gradient_params: - # the whole SummedOp might be multiplied by a parameter of interest - if gradient_param == hamiltonian.coeff: - gradient = self._calc_term_gradient(hamiltonian, hamiltonian, - initial_state, observable, t_param, - time, hamiltonian_value_dict) - gradients[gradient_param] += gradient for hamiltonian_term in hamiltonian.oplist: if gradient_param in hamiltonian_term.parameters: - gradient = self._calc_term_gradient(hamiltonian, hamiltonian_term, - initial_state, observable, t_param, - time, hamiltonian_value_dict) - gradients[gradient_param] += gradient - # PauliSumOp has a coefficient which is complex or a parameter. If complex, it will be - # skipped for a particular gradient as expected. - elif isinstance(hamiltonian, PauliSumOp): - for gradient_param in gradient_params: - # the whole PauliSumOp might be multiplied by a parameter of interest - if gradient_param == hamiltonian.coeff: - gradient = self._calc_term_gradient(hamiltonian, hamiltonian, - initial_state, observable, t_param, - time, hamiltonian_value_dict) - gradients[gradient_param] += gradient - for hamiltonian_term in hamiltonian: - if gradient_param in hamiltonian_term.parameters: - gradient = self._calc_term_gradient(hamiltonian, - hamiltonian_term.primitive, - initial_state, observable, t_param, - time, hamiltonian_value_dict) + gradient = self._calc_term_gradient( + hamiltonian, + hamiltonian_term, + initial_state, + observable, + t_param, + time, + hamiltonian_value_dict, + ) gradients[gradient_param] += gradient return gradients + elif any(gradient_params) not in (hamiltonian.parameters or [t_param]): - raise ValueError("gradient_params provided that are not found in hamiltonian.params " - "and not a t_param.") + raise ValueError( + "gradient_params provided that are not found in hamiltonian.params " + "and not a t_param." + ) - def _calc_term_gradient(self, hamiltonian, - hamiltonian_term: Union[OperatorBase, PauliSumOp, SummedOp], - initial_state, observable, t_param, - time, hamiltonian_value_dict): + def _calc_time_gradient_finite_diff( + self, hamiltonian, hamiltonian_value_dict, initial_state, observable, t_param, time + ): + epsilon = 0.01 + hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) + evolved_state1 = self.evolve(hamiltonian, time + epsilon, initial_state, t_param=t_param) + evolved_state2 = self.evolve(hamiltonian, time - epsilon, initial_state, t_param=t_param) + expected_val_1 = ~StateFn(observable) @ evolved_state1 + expected_val_2 = ~StateFn(observable) @ evolved_state2 + finite_difference = (expected_val_1 - expected_val_2) / (2 * epsilon) + return finite_difference + + def _calc_term_gradient( + self, + hamiltonian, + hamiltonian_term: Union[OperatorBase, PauliSumOp, SummedOp], + initial_state, + observable, + t_param, + time, + hamiltonian_value_dict, + ): hamiltonian_term = self._try_binding_params(hamiltonian_term, hamiltonian_value_dict) custom_observable = commutator(1j * time * hamiltonian_term, observable) hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) - evolved_state = self.evolve(hamiltonian, time, initial_state, t_param, - hamiltonian_value_dict=hamiltonian_value_dict) + evolved_state = self.evolve( + hamiltonian, time, initial_state, t_param, hamiltonian_value_dict=hamiltonian_value_dict + ) gradient = ~StateFn(custom_observable) @ evolved_state gradient = gradient.eval() return gradient - def _validate_hamiltonian_form(self, hamiltonian: Union[OperatorBase, PauliSumOp, SummedOp]): + def _validate_hamiltonian_form(self, hamiltonian: SummedOp): if isinstance(hamiltonian, SummedOp): + if isinstance(hamiltonian.coeff, ParameterExpression): + raise ValueError( + "The coefficient multiplying the whole Hamiltonian cannot be a " + "ParameterExpression." + ) for op in hamiltonian.oplist: - if not isinstance(op.coeff, ParameterExpression): + if not self._is_linear_with_single_param(op): raise ValueError( - "Term of a Hamiltonian has a coefficient that is not a " - "ParameterExpression. It is not allowed.") - if len(op.coeff.parameters) > 1: - raise ValueError( - "Term of a Hamiltonian has a coefficient that depends on several " - "parameters. Only dependence on a single parameter is allowed.") - # TODO check if param linear + "Hamiltonian term has a coefficient that is not a linear function of a " + "single parameter. It is not supported." + ) - elif isinstance(hamiltonian, PauliSumOp): - for op in hamiltonian: - if len(op.coeffs) > 1: - raise ValueError( - "Term of a Hamiltonian has multiple coefficients. It is not allowed.") - if not isinstance(op.coeffs[0], ParameterExpression): - raise ValueError( - "Term of a Hamiltonian has a coefficient that is not a " - "ParameterExpression. It is not allowed.") - if len(op.coeffs[0].parameters) > 1: - raise ValueError( - "Term of a Hamiltonian has a coefficient that depends on several " - "parameters. Only dependence on a single parameter is allowed.") - # TODO check if param linear else: - raise ValueError("Hamiltonian not a SummedOp or PauliSumOp") + raise ValueError("Hamiltonian not a SummedOp which is the only option supported.") + + def _is_linear_with_single_param(self, operator: OperatorBase): + if ( + not isinstance(operator.coeff, ParameterExpression) + and not isinstance(operator.coeff, Parameter) + or len(operator.coeff.parameters) == 0 + ): + return True + if len(operator.coeff.parameters) > 1: + raise ValueError( + "Term of a Hamiltonian has a coefficient that depends on several " + "parameters. Only dependence on a single parameter is allowed." + ) + single_parameter_expression = operator.coeff + parameter = list(single_parameter_expression.parameters)[0] + gradient = single_parameter_expression.gradient(parameter) + return isinstance(gradient, float) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index a561bb87966c..dc5ca3233979 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -16,12 +16,10 @@ from numpy.testing import assert_raises from qiskit import QiskitError -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations \ - .trotter_mode_enum import ( +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( TrotterModeEnum, ) -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte \ - import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( TrotterQrte, ) from qiskit.quantum_info import Statevector @@ -34,7 +32,10 @@ Zero, VectorStateFn, StateFn, - MatrixOp, I, PauliSumOp, Y, + MatrixOp, + I, + PauliSumOp, + Y, ) @@ -153,25 +154,31 @@ def test_trotter_qrte_trotter_binding_missing_param(self): def test_trotter_qrte_gradient_summed_op_qdrift(self): """Test for trotter qrte gradient with SummedOp and QDrift.""" - theta1 = Parameter('theta1') - theta2 = Parameter('theta2') + theta1 = Parameter("theta1") + theta2 = Parameter("theta2") operator = theta1 * (I ^ Z ^ I) + theta2 * (Z ^ I ^ I) mode = TrotterModeEnum.QDRIFT trotter_qrte = TrotterQrte(mode) initial_state = Zero time = 5 gradient_object = None - observable = Z^Y^Z + observable = Z ^ Y ^ Z value_dict = {theta1: 2, theta2: 3} - gradient = trotter_qrte.gradient(operator, time, initial_state, gradient_object, - observable, hamiltonian_value_dict=value_dict, - gradient_params=[theta1, theta2]) - - print(gradient) + gradient = trotter_qrte.gradient( + operator, + time, + initial_state, + gradient_object, + observable, + hamiltonian_value_dict=value_dict, + gradient_params=[theta1, theta2], + ) + expected_gradient = {theta1: 0j, theta2: 0j} + np.testing.assert_equal(gradient, expected_gradient) def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): """Test for trotter qrte gradient with SummedOp and QDrift; missing param.""" - theta1 = Parameter('theta1') + theta1 = Parameter("theta1") operator = theta1 * (I ^ Z ^ I) + 5 * (Z ^ I ^ I) mode = TrotterModeEnum.QDRIFT trotter_qrte = TrotterQrte(mode) @@ -180,15 +187,22 @@ def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): gradient_object = None observable = Z value_dict = {theta1: 2} - with assert_raises(ValueError): - _ = trotter_qrte.gradient(operator, time, initial_state, gradient_object, - observable, hamiltonian_value_dict=value_dict, - gradient_params=[theta1]) + gradient = trotter_qrte.gradient( + operator, + time, + initial_state, + gradient_object, + observable, + hamiltonian_value_dict=value_dict, + gradient_params=[theta1], + ) + expected_gradient = {theta1: 0j} + np.testing.assert_equal(gradient, expected_gradient) def test_trotter_qrte_gradient_summed_op_qdrift_t_param_grad(self): """Test for trotter qrte gradient with SummedOp and QDrift; missing param.""" - t_param = Parameter('t_param') - theta1 = Parameter('theta1') + t_param = Parameter("t_param") + theta1 = Parameter("theta1") operator = t_param * (I ^ Z ^ I) + theta1 * (Z ^ I ^ I) mode = TrotterModeEnum.QDRIFT trotter_qrte = TrotterQrte(mode) @@ -197,12 +211,19 @@ def test_trotter_qrte_gradient_summed_op_qdrift_t_param_grad(self): gradient_object = None observable = Z value_dict = {theta1: 2, t_param: 5} - gradient = trotter_qrte.gradient(operator, time, initial_state, gradient_object, - observable, t_param=t_param, - hamiltonian_value_dict=value_dict, - gradient_params=[t_param]) + gradient = trotter_qrte.gradient( + operator, + time, + initial_state, + gradient_object, + observable, + t_param=t_param, + hamiltonian_value_dict=value_dict, + gradient_params=[t_param], + ) - print(gradient) + expected_gradient = 4.7628567756419216e-12 + 0j + np.testing.assert_equal(gradient, expected_gradient) if __name__ == "__main__": From 6ae7d567b5de63f8905587c1a087e61fe3061b3e Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 28 Jul 2021 13:30:35 +0200 Subject: [PATCH 011/145] trotter_qrte.py improvements; unit tests extended. --- .../trotterization/trotter_qrte.py | 62 ++++---- .../trotterization/test_trotter_qrte.py | 132 ++++++++++++++++-- 2 files changed, 156 insertions(+), 38 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index 4fac6cc646b1..74dd88d29631 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -9,15 +9,14 @@ # 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. +import numbers from collections import defaultdict from typing import Union, Optional -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ - .trotter_mode_enum import ( +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( TrotterModeEnum, ) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations\ - .trotterization_factory import ( +from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_factory import ( TrotterizationFactory, ) from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte @@ -112,32 +111,41 @@ def gradient( ) self._validate_hamiltonian_form(hamiltonian) + param_set = set(gradient_params) + if t_param is not None: + param_set.add(t_param) - if t_param in gradient_params: - finite_difference = self._calc_time_gradient_finite_diff( - hamiltonian, hamiltonian_value_dict, initial_state, observable, t_param, time - ) - return finite_difference.eval() - elif set(gradient_params) == set(hamiltonian.parameters): + if param_set.issubset(set(hamiltonian.parameters)): gradients = defaultdict(float) if isinstance(hamiltonian, SummedOp): - for gradient_param in gradient_params: + for gradient_param in param_set: for hamiltonian_term in hamiltonian.oplist: if gradient_param in hamiltonian_term.parameters: - gradient = self._calc_term_gradient( - hamiltonian, - hamiltonian_term, - initial_state, - observable, - t_param, - time, - hamiltonian_value_dict, - ) - gradients[gradient_param] += gradient + if gradient_param == t_param: + finite_difference = self._calc_time_gradient_finite_diff( + hamiltonian, + hamiltonian_value_dict, + initial_state, + observable, + t_param, + time, + ) + gradients[gradient_param] += finite_difference.eval() + else: + gradient = self._calc_term_gradient( + hamiltonian, + hamiltonian_term, + initial_state, + observable, + t_param, + time, + hamiltonian_value_dict, + ) + gradients[gradient_param] += gradient return gradients - elif any(gradient_params) not in (hamiltonian.parameters or [t_param]): + else: raise ValueError( "gradient_params provided that are not found in hamiltonian.params " "and not a t_param." @@ -170,7 +178,11 @@ def _calc_term_gradient( hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) evolved_state = self.evolve( - hamiltonian, time, initial_state, t_param, hamiltonian_value_dict=hamiltonian_value_dict + hamiltonian, + time, + initial_state, + t_param=t_param, + hamiltonian_value_dict=hamiltonian_value_dict, ) gradient = ~StateFn(custom_observable) @ evolved_state @@ -196,7 +208,7 @@ def _validate_hamiltonian_form(self, hamiltonian: SummedOp): def _is_linear_with_single_param(self, operator: OperatorBase): if ( - not isinstance(operator.coeff, ParameterExpression) + not isinstance(operator.coeff, ParameterExpression) and not isinstance(operator.coeff, Parameter) or len(operator.coeff.parameters) == 0 ): @@ -209,4 +221,4 @@ def _is_linear_with_single_param(self, operator: OperatorBase): single_parameter_expression = operator.coeff parameter = list(single_parameter_expression.parameters)[0] gradient = single_parameter_expression.gradient(parameter) - return isinstance(gradient, float) + return isinstance(gradient, numbers.Number) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index dc5ca3233979..3903d4478755 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -15,7 +15,6 @@ import numpy as np from numpy.testing import assert_raises -from qiskit import QiskitError from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( TrotterModeEnum, ) @@ -34,7 +33,6 @@ StateFn, MatrixOp, I, - PauliSumOp, Y, ) @@ -153,7 +151,7 @@ def test_trotter_qrte_trotter_binding_missing_param(self): _ = trotter_qrte.evolve(operator, 1, initial_state) def test_trotter_qrte_gradient_summed_op_qdrift(self): - """Test for trotter qrte gradient with SummedOp and QDrift.""" + """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" theta1 = Parameter("theta1") theta2 = Parameter("theta2") operator = theta1 * (I ^ Z ^ I) + theta2 * (Z ^ I ^ I) @@ -176,6 +174,85 @@ def test_trotter_qrte_gradient_summed_op_qdrift(self): expected_gradient = {theta1: 0j, theta2: 0j} np.testing.assert_equal(gradient, expected_gradient) + # TODO fails due to Terra bug + def test_trotter_qrte_gradient_summed_op_qdrift_2(self): + """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" + theta1 = Parameter("theta1") + theta2 = Parameter("theta2") + operator = theta1 * (-1j * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) + mode = TrotterModeEnum.QDRIFT + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + time = 5 + gradient_object = None + observable = X ^ Y ^ Z + value_dict = {theta1: 2, theta2: 3} + gradient = trotter_qrte.gradient( + operator, + time, + initial_state, + gradient_object, + observable, + hamiltonian_value_dict=value_dict, + gradient_params=[theta1, theta2], + ) + expected_gradient = {theta1: 0j, theta2: 0j} + print(gradient) + # np.testing.assert_equal(gradient, expected_gradient) + + # TODO fails due to Terra bug + def test_trotter_qrte_gradient_summed_op_qdrift_3(self): + """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" + theta1 = Parameter("theta1") + theta2 = Parameter("theta2") + operator = theta1 * (-1j * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) + mode = TrotterModeEnum.QDRIFT + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + time = 5 + gradient_object = None + observable = -1j * (X ^ Y ^ Z) + value_dict = {theta1: 2, theta2: 3} + gradient = trotter_qrte.gradient( + operator, + time, + initial_state, + gradient_object, + observable, + hamiltonian_value_dict=value_dict, + gradient_params=[theta1, theta2], + ) + expected_gradient = {theta1: 0j, theta2: 0j} + print(gradient) + # np.testing.assert_equal(gradient, expected_gradient) + + # TODO fails due to Terra bug + def test_trotter_qrte_gradient_summed_op_qdrift_4(self): + """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators + with complex parameter binding.""" + theta1 = Parameter("theta1") + theta2 = Parameter("theta2") + operator = theta1 * (Z) + theta2 * (Y) + mode = TrotterModeEnum.QDRIFT + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + time = 5 + gradient_object = None + observable = X + value_dict = {theta1: 2, theta2: 3} + gradient = trotter_qrte.gradient( + operator, + time, + initial_state, + gradient_object, + observable, + hamiltonian_value_dict=value_dict, + gradient_params=[theta1, theta2], + ) + expected_gradient = {theta1: 0j, theta2: 0j} + print(gradient) + # np.testing.assert_equal(gradient, expected_gradient) + def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): """Test for trotter qrte gradient with SummedOp and QDrift; missing param.""" theta1 = Parameter("theta1") @@ -188,19 +265,20 @@ def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): observable = Z value_dict = {theta1: 2} gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - hamiltonian_value_dict=value_dict, - gradient_params=[theta1], - ) + operator, + time, + initial_state, + gradient_object, + observable, + hamiltonian_value_dict=value_dict, + gradient_params=[theta1], + ) expected_gradient = {theta1: 0j} np.testing.assert_equal(gradient, expected_gradient) def test_trotter_qrte_gradient_summed_op_qdrift_t_param_grad(self): - """Test for trotter qrte gradient with SummedOp and QDrift; missing param.""" + """Test for trotter qrte gradient with SummedOp and QDrift; gradient not on all + parameters.""" t_param = Parameter("t_param") theta1 = Parameter("theta1") operator = t_param * (I ^ Z ^ I) + theta1 * (Z ^ I ^ I) @@ -222,9 +300,37 @@ def test_trotter_qrte_gradient_summed_op_qdrift_t_param_grad(self): gradient_params=[t_param], ) - expected_gradient = 4.7628567756419216e-12 + 0j + expected_gradient = {t_param: (4.7628567756419216e-12 + 0j)} np.testing.assert_equal(gradient, expected_gradient) + def test_trotter_qrte_gradient_summed_op_qdrift_t_param_theta(self): + """Test for trotter qrte gradient with SummedOp and QDrift; gradient on t_param and + another parameter.""" + t_param = Parameter("t_param") + theta1 = Parameter("theta1") + operator = t_param * (I ^ Z ^ I) + theta1 * (Z ^ I ^ I) + mode = TrotterModeEnum.QDRIFT + trotter_qrte = TrotterQrte(mode) + initial_state = Zero + time = 5 + gradient_object = None + observable = Z + value_dict = {theta1: 2, t_param: 5} + gradient = trotter_qrte.gradient( + operator, + time, + initial_state, + gradient_object, + observable, + t_param=t_param, + hamiltonian_value_dict=value_dict, + gradient_params=[t_param, theta1], + ) + + # expected_gradient = {t_param: (4.7628567756419216e-12+0j)} + # np.testing.assert_equal(gradient, expected_gradient) + print(gradient) + if __name__ == "__main__": unittest.main() From 93fe39da20f9f08e51fb9cf8ff875e7c867d7283 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 3 Sep 2021 14:03:41 +0200 Subject: [PATCH 012/145] Trotter test fix. --- .../real/implementations/trotterization/test_trotter_qrte.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 3903d4478755..81d01e644456 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -179,7 +179,7 @@ def test_trotter_qrte_gradient_summed_op_qdrift_2(self): """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" theta1 = Parameter("theta1") theta2 = Parameter("theta2") - operator = theta1 * (-1j * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) + operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) mode = TrotterModeEnum.QDRIFT trotter_qrte = TrotterQrte(mode) initial_state = Zero @@ -205,7 +205,7 @@ def test_trotter_qrte_gradient_summed_op_qdrift_3(self): """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" theta1 = Parameter("theta1") theta2 = Parameter("theta2") - operator = theta1 * (-1j * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) + operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) mode = TrotterModeEnum.QDRIFT trotter_qrte = TrotterQrte(mode) initial_state = Zero From 4a16874154a135ed43891a0655324735fc7a4da8 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 3 Sep 2021 14:35:42 +0200 Subject: [PATCH 013/145] Fixed float and complex handling. --- qiskit/opflow/primitive_ops/primitive_op.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qiskit/opflow/primitive_ops/primitive_op.py b/qiskit/opflow/primitive_ops/primitive_op.py index e84e7c084794..6b16d84412e3 100644 --- a/qiskit/opflow/primitive_ops/primitive_op.py +++ b/qiskit/opflow/primitive_ops/primitive_op.py @@ -246,7 +246,11 @@ def assign_parameters(self, param_dict: dict) -> OperatorBase: return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict]) if self.coeff.parameters <= set(unrolled_dict.keys()): binds = {param: unrolled_dict[param] for param in self.coeff.parameters} - param_value = float(self.coeff.bind(binds)) + param_value = self.coeff.bind(binds) + if np.imag(complex(param_value)) == 0: + param_value = float(param_value) + else: + param_value = complex(param_value) return self.__class__(self.primitive, coeff=param_value) # Nothing to collapse here. From 5e1536eef5a6cfa25f4d007476e227fbfe366f49 Mon Sep 17 00:00:00 2001 From: "acv@zurich.ibm.com" Date: Tue, 7 Dec 2021 16:59:57 +0100 Subject: [PATCH 014/145] qrte with product formula --- .../trotterization/trotter_qrte.py | 84 +++- .../trotterization/test_trotter_qrte.py | 400 +++++++++--------- 2 files changed, 267 insertions(+), 217 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index 74dd88d29631..fdc0d77439c3 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -9,53 +9,99 @@ # 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. + +"""An algorithm to implement a Trotterization real time-evolution.""" + import numbers from collections import defaultdict from typing import Union, Optional -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( - TrotterModeEnum, -) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_factory import ( - TrotterizationFactory, -) from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import ( EvolutionGradientResult, ) from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult from qiskit.circuit import Parameter, ParameterExpression -from qiskit.opflow import OperatorBase, StateFn, Gradient, commutator, SummedOp, PauliSumOp, PauliOp +from qiskit.opflow import OperatorBase, StateFn, Gradient, commutator, SummedOp, PauliSumOp, \ + PauliOp, CircuitOp +from qiskit.circuit.library import PauliEvolutionGate +from qiskit.synthesis import ProductFormula, LieTrotter class TrotterQrte(Qrte): - def __init__(self, mode: TrotterModeEnum, reps: int = 1): - self._mode = mode - self._reps = reps + """ TODO write documentation + + Examples: + + .. jupyter-execute:: + + from qiskit.opflow import X, Y, Zero + from qiskit.algorithms.quantum_time_evolution.real.implementations.\ + trotterization.trotter_qrte import TrotterQrte + + operator = X + Z + # LieTrotter with 1 rep + trotter_qrte = TrotterQrte() + initial_state = Zero + evolved_state = trotter_qrte.evolve(operator, 1, initial_state) + """ + def __init__(self, product_formula: ProductFormula = LieTrotter()): + """ + Args: + product_formula: A Lie-Trotter-Suzuki product formula. The default is the Lie-Trotter + first order product formula with a single repetition. + """ + self.product_formula = product_formula def evolve( self, - hamiltonian: OperatorBase, + hamiltonian, time: float, initial_state: StateFn = None, observable: OperatorBase = None, t_param: Parameter = None, hamiltonian_value_dict=None, ) -> EvolutionResult: + """ + Args: + hamiltonian (Pauli | PauliOp | SparsePauliOp | PauliSumOp | list): + The operator to evolve. Can also be provided as list of non-commuting + operators where the elements are sums of commuting operators. + For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. + time: The evolution time. + initial_state: TODO. + observable: TODO. + t_param: Not accepted in this class. + hamiltonian_value_dict: TODO + + Returns: + The evolved hamiltonian applied to either an initial state or an observable. + + Raises: + ValueError: If t_param is not set to None (feature not currently supported). + """ + if t_param is not None: + raise ValueError("TrotterQrte does not accept a time dependent hamiltonian," + "t_param should be set to None.") hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) self._validate_input(initial_state, observable) - - trotter = TrotterizationFactory.build(self._mode, self._reps) - trotterized_evolution_op = trotter.build(time * hamiltonian) + # the evolution gate + evolution_gate = CircuitOp(PauliEvolutionGate(hamiltonian, time, + synthesis=self.product_formula)) if initial_state is not None: - return (trotterized_evolution_op @ initial_state).eval() + return (evolution_gate @ initial_state).eval() if observable is not None: - return trotterized_evolution_op.adjoint() @ observable @ trotterized_evolution_op + # TODO Temporary patch due to terra bug + evolution_gate_adjoint = CircuitOp(PauliEvolutionGate(hamiltonian[::-1], -time, + synthesis=self.product_formula)) + # return evolution_gate.adjoint() @ observable @ evolution_gate + return evolution_gate_adjoint @ observable @ evolution_gate def _try_binding_params(self, hamiltonian, hamiltonian_value_dict): - # PauliSumOp does not allow parametrized coefficients + # PauliSumOp does not allow parametrized coefficients but after binding the parameters + # we need to convert it into a PauliSumOp for the PauliEvolutionGate if isinstance(hamiltonian, SummedOp): op_list = [] for op in hamiltonian.oplist: @@ -69,7 +115,7 @@ def _try_binding_params(self, hamiltonian, hamiltonian_value_dict): f"these parameters encountered: {op_bound.parameters}." ) op_list.append(op_bound) - return SummedOp(op_list) + return sum(op_list) # for an observable, we might have an OperatorBase... TODO elif isinstance(hamiltonian, PauliOp): return hamiltonian.bind_parameters(hamiltonian_value_dict) @@ -82,7 +128,7 @@ def _validate_input(self, initial_state, observable): "TrotterQrte requires an initial state or an observable to be evolved; None " "provided." ) - elif initial_state is not None and observable is not None: + if initial_state is not None and observable is not None: raise ValueError( "TrotterQrte requires an initial state or an observable to be evolved; both " "provided." diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 81d01e644456..47541ff89a35 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -9,21 +9,20 @@ # 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. + """ Test Trotter Qrte. """ + import unittest +from test.python.opflow import QiskitOpflowTestCase import numpy as np from numpy.testing import assert_raises +from scipy.linalg import expm -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( - TrotterModeEnum, -) -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( - TrotterQrte, -) +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte \ + import TrotterQrte from qiskit.quantum_info import Statevector from qiskit.utils import algorithm_globals -from test.python.opflow import QiskitOpflowTestCase from qiskit.circuit import Parameter from qiskit.opflow import ( X, @@ -35,6 +34,7 @@ I, Y, ) +from qiskit.synthesis import SuzukiTrotter, QDrift class TestTrotterQrte(QiskitOpflowTestCase): @@ -43,55 +43,58 @@ class TestTrotterQrte(QiskitOpflowTestCase): def test_trotter_qrte_trotter(self): """Test for trotter qrte.""" operator = X + Z - mode = TrotterModeEnum.TROTTER - trotter_qrte = TrotterQrte(mode) + # LieTrotter with 1 rep + trotter_qrte = TrotterQrte() initial_state = Zero evolved_state = trotter_qrte.evolve(operator, 1, initial_state) - expected_evolved_state = VectorStateFn( - Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], dims=(2,)) - ) + # Calculate the expected state + expected_state = expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ \ + initial_state.to_matrix() + expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) np.testing.assert_equal(evolved_state, expected_evolved_state) def test_trotter_qrte_trotter_2(self): """Test for trotter qrte.""" - operator = X + Z - mode = TrotterModeEnum.TROTTER - trotter_qrte = TrotterQrte(mode) - initial_state = StateFn([1, 0]) + operator = [(X ^ Y) + (Y ^ X), (Z ^ Z) + (Z ^ I) + (I ^ Z), Y ^ Y] + # LieTrotter with 1 rep + trotter_qrte = TrotterQrte() + initial_state = StateFn([1, 0, 0, 0]) evolved_state = trotter_qrte.evolve(operator, 1, initial_state) - expected_evolved_state = VectorStateFn( - Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], dims=(2,)) - ) + # Calculate the expected state + expected_state = initial_state.to_matrix() + for op in operator: + expected_state = expm(-1j * op.to_matrix()) @ expected_state + expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2, 2))) np.testing.assert_equal(evolved_state, expected_evolved_state) def test_trotter_qrte_trotter_observable(self): """Test for trotter qrte with an observable.""" operator = X + Z - mode = TrotterModeEnum.TROTTER - trotter_qrte = TrotterQrte(mode) + # LieTrotter with 1 rep + trotter_qrte = TrotterQrte() observable = X evolved_observable = trotter_qrte.evolve(operator, 1, observable=observable) evolved_observable = evolved_observable.to_matrix_op() - expected_evolved_observable = MatrixOp( - [ - [2.99928030e-17 - 4.67110231e-17j, -4.16146837e-01 + 9.09297427e-01j], - [-4.16146837e-01 - 9.09297427e-01j, 0.00000000e00 + 0.00000000e00j], - ] - ) + # Calculate the expected operator + expected_op = expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) + expected_evolved_observable = MatrixOp(expected_op.conj().T @ observable.to_matrix() + @ expected_op) + np.testing.assert_equal(evolved_observable, expected_evolved_observable) def test_trotter_qrte_suzuki(self): """Test for trotter qrte with Suzuki.""" operator = X + Z - mode = TrotterModeEnum.SUZUKI - trotter_qrte = TrotterQrte(mode) + # 2nd order Suzuki with 1 rep + trotter_qrte = TrotterQrte(SuzukiTrotter()) initial_state = Zero evolved_state = trotter_qrte.evolve(operator, 1, initial_state) - expected_evolved_state = VectorStateFn( - Statevector([0.29192658 - 0.45464871j, 0.0 - 0.84147098j], dims=(2,)) - ) + # Calculate the expected state + expected_state = expm(-1j * X.to_matrix() * 0.5) @ expm(-1j * Z.to_matrix()) \ + @ expm(-1j * X.to_matrix() * 0.5) @ initial_state.to_matrix() + expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) np.testing.assert_equal(evolved_state, expected_evolved_state) @@ -99,43 +102,46 @@ def test_trotter_qrte_qdrift(self): """Test for trotter qrte with QDrift.""" algorithm_globals.random_seed = 0 operator = X + Z - mode = TrotterModeEnum.QDRIFT - trotter_qrte = TrotterQrte(mode) + # QDrift with one repetition + trotter_qrte = TrotterQrte(QDrift()) initial_state = Zero evolved_state = trotter_qrte.evolve(operator, 1, initial_state) - expected_evolved_state = VectorStateFn( - Statevector([0.23071786 - 0.69436148j, -0.4646314 - 0.49874749j], dims=(2,)) - ) - - np.testing.assert_equal(evolved_state, expected_evolved_state) - - def test_trotter_qrte_trotter_binding(self): - """Test for trotter qrte with binding.""" - t_param = Parameter("t") - operator = X * t_param + Z - hamiltonian_value_dict = {t_param: 1} - mode = TrotterModeEnum.TROTTER - trotter_qrte = TrotterQrte(mode) - initial_state = Zero - evolved_state = trotter_qrte.evolve( - operator, - 1, - initial_state, - t_param=t_param, - hamiltonian_value_dict=hamiltonian_value_dict, - ) - expected_evolved_state = VectorStateFn( - Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], dims=(2,)) - ) + sampled_ops = [Z, X, X, X, Z, Z, Z, Z] + evo_time = 0.25 + # Calculate the expected state + expected_state = initial_state.to_matrix() + for op in sampled_ops: + expected_state = expm(-1j * op.to_matrix() * evo_time) @ expected_state + expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) np.testing.assert_equal(evolved_state, expected_evolved_state) + # TODO this test is disabled for the time being - no t_param for TrotterQrte + # def test_trotter_qrte_trotter_binding(self): + # """Test for trotter qrte with binding.""" + # t_param = Parameter("t") + # operator = X * t_param + Z + # hamiltonian_value_dict = {t_param: 1} + # trotter_qrte = TrotterQrte() + # initial_state = Zero + # evolved_state = trotter_qrte.evolve( + # operator, + # 1, + # initial_state, + # t_param=t_param, + # hamiltonian_value_dict=hamiltonian_value_dict, + # ) + # expected_evolved_state = VectorStateFn( + # Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], dims=(2,)) + # ) + # + # np.testing.assert_equal(evolved_state, expected_evolved_state) + # def test_trotter_qrte_trotter_binding_missing_dict(self): """Test for trotter qrte with binding and missing dictionary..""" t_param = Parameter("t") operator = X * t_param + Z - mode = TrotterModeEnum.TROTTER - trotter_qrte = TrotterQrte(mode) + trotter_qrte = TrotterQrte() initial_state = Zero with assert_raises(ValueError): _ = trotter_qrte.evolve(operator, 1, initial_state, t_param=t_param) @@ -144,8 +150,7 @@ def test_trotter_qrte_trotter_binding_missing_param(self): """Test for trotter qrte with binding and missing param.""" t_param = Parameter("t") operator = X * t_param + Z - mode = TrotterModeEnum.TROTTER - trotter_qrte = TrotterQrte(mode) + trotter_qrte = TrotterQrte() initial_state = Zero with assert_raises(ValueError): _ = trotter_qrte.evolve(operator, 1, initial_state) @@ -155,8 +160,7 @@ def test_trotter_qrte_gradient_summed_op_qdrift(self): theta1 = Parameter("theta1") theta2 = Parameter("theta2") operator = theta1 * (I ^ Z ^ I) + theta2 * (Z ^ I ^ I) - mode = TrotterModeEnum.QDRIFT - trotter_qrte = TrotterQrte(mode) + trotter_qrte = TrotterQrte(QDrift()) initial_state = Zero time = 5 gradient_object = None @@ -174,91 +178,90 @@ def test_trotter_qrte_gradient_summed_op_qdrift(self): expected_gradient = {theta1: 0j, theta2: 0j} np.testing.assert_equal(gradient, expected_gradient) - # TODO fails due to Terra bug - def test_trotter_qrte_gradient_summed_op_qdrift_2(self): - """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" - theta1 = Parameter("theta1") - theta2 = Parameter("theta2") - operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) - mode = TrotterModeEnum.QDRIFT - trotter_qrte = TrotterQrte(mode) - initial_state = Zero - time = 5 - gradient_object = None - observable = X ^ Y ^ Z - value_dict = {theta1: 2, theta2: 3} - gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - hamiltonian_value_dict=value_dict, - gradient_params=[theta1, theta2], - ) - expected_gradient = {theta1: 0j, theta2: 0j} - print(gradient) - # np.testing.assert_equal(gradient, expected_gradient) - - # TODO fails due to Terra bug - def test_trotter_qrte_gradient_summed_op_qdrift_3(self): - """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" - theta1 = Parameter("theta1") - theta2 = Parameter("theta2") - operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) - mode = TrotterModeEnum.QDRIFT - trotter_qrte = TrotterQrte(mode) - initial_state = Zero - time = 5 - gradient_object = None - observable = -1j * (X ^ Y ^ Z) - value_dict = {theta1: 2, theta2: 3} - gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - hamiltonian_value_dict=value_dict, - gradient_params=[theta1, theta2], - ) - expected_gradient = {theta1: 0j, theta2: 0j} - print(gradient) - # np.testing.assert_equal(gradient, expected_gradient) - - # TODO fails due to Terra bug - def test_trotter_qrte_gradient_summed_op_qdrift_4(self): - """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators - with complex parameter binding.""" - theta1 = Parameter("theta1") - theta2 = Parameter("theta2") - operator = theta1 * (Z) + theta2 * (Y) - mode = TrotterModeEnum.QDRIFT - trotter_qrte = TrotterQrte(mode) - initial_state = Zero - time = 5 - gradient_object = None - observable = X - value_dict = {theta1: 2, theta2: 3} - gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - hamiltonian_value_dict=value_dict, - gradient_params=[theta1, theta2], - ) - expected_gradient = {theta1: 0j, theta2: 0j} - print(gradient) - # np.testing.assert_equal(gradient, expected_gradient) + # # TODO fails due to Terra bug + # def test_trotter_qrte_gradient_summed_op_qdrift_2(self): + # """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" + # theta1 = Parameter("theta1") + # theta2 = Parameter("theta2") + # operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) + # mode = TrotterModeEnum.QDRIFT + # trotter_qrte = TrotterQrte(mode) + # initial_state = Zero + # time = 5 + # gradient_object = None + # observable = X ^ Y ^ Z + # value_dict = {theta1: 2, theta2: 3} + # gradient = trotter_qrte.gradient( + # operator, + # time, + # initial_state, + # gradient_object, + # observable, + # hamiltonian_value_dict=value_dict, + # gradient_params=[theta1, theta2], + # ) + # expected_gradient = {theta1: 0j, theta2: 0j} + # print(gradient) + # # np.testing.assert_equal(gradient, expected_gradient) + # + # # TODO fails due to Terra bug + # def test_trotter_qrte_gradient_summed_op_qdrift_3(self): + # """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" + # theta1 = Parameter("theta1") + # theta2 = Parameter("theta2") + # operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) + # mode = TrotterModeEnum.QDRIFT + # trotter_qrte = TrotterQrte(mode) + # initial_state = Zero + # time = 5 + # gradient_object = None + # observable = -1j * (X ^ Y ^ Z) + # value_dict = {theta1: 2, theta2: 3} + # gradient = trotter_qrte.gradient( + # operator, + # time, + # initial_state, + # gradient_object, + # observable, + # hamiltonian_value_dict=value_dict, + # gradient_params=[theta1, theta2], + # ) + # expected_gradient = {theta1: 0j, theta2: 0j} + # print(gradient) + # # np.testing.assert_equal(gradient, expected_gradient) + # + # # TODO fails due to Terra bug + # def test_trotter_qrte_gradient_summed_op_qdrift_4(self): + # """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators + # with complex parameter binding.""" + # theta1 = Parameter("theta1") + # theta2 = Parameter("theta2") + # operator = theta1 * (Z) + theta2 * (Y) + # mode = TrotterModeEnum.QDRIFT + # trotter_qrte = TrotterQrte(mode) + # initial_state = Zero + # time = 5 + # gradient_object = None + # observable = X + # value_dict = {theta1: 2, theta2: 3} + # gradient = trotter_qrte.gradient( + # operator, + # time, + # initial_state, + # gradient_object, + # observable, + # hamiltonian_value_dict=value_dict, + # gradient_params=[theta1, theta2], + # ) + # expected_gradient = {theta1: 0j, theta2: 0j} + # print(gradient) + # # np.testing.assert_equal(gradient, expected_gradient) def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): """Test for trotter qrte gradient with SummedOp and QDrift; missing param.""" theta1 = Parameter("theta1") operator = theta1 * (I ^ Z ^ I) + 5 * (Z ^ I ^ I) - mode = TrotterModeEnum.QDRIFT - trotter_qrte = TrotterQrte(mode) + trotter_qrte = TrotterQrte(QDrift()) initial_state = Zero time = 5 gradient_object = None @@ -276,60 +279,61 @@ def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): expected_gradient = {theta1: 0j} np.testing.assert_equal(gradient, expected_gradient) - def test_trotter_qrte_gradient_summed_op_qdrift_t_param_grad(self): - """Test for trotter qrte gradient with SummedOp and QDrift; gradient not on all - parameters.""" - t_param = Parameter("t_param") - theta1 = Parameter("theta1") - operator = t_param * (I ^ Z ^ I) + theta1 * (Z ^ I ^ I) - mode = TrotterModeEnum.QDRIFT - trotter_qrte = TrotterQrte(mode) - initial_state = Zero - time = 5 - gradient_object = None - observable = Z - value_dict = {theta1: 2, t_param: 5} - gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - t_param=t_param, - hamiltonian_value_dict=value_dict, - gradient_params=[t_param], - ) - - expected_gradient = {t_param: (4.7628567756419216e-12 + 0j)} - np.testing.assert_equal(gradient, expected_gradient) - - def test_trotter_qrte_gradient_summed_op_qdrift_t_param_theta(self): - """Test for trotter qrte gradient with SummedOp and QDrift; gradient on t_param and - another parameter.""" - t_param = Parameter("t_param") - theta1 = Parameter("theta1") - operator = t_param * (I ^ Z ^ I) + theta1 * (Z ^ I ^ I) - mode = TrotterModeEnum.QDRIFT - trotter_qrte = TrotterQrte(mode) - initial_state = Zero - time = 5 - gradient_object = None - observable = Z - value_dict = {theta1: 2, t_param: 5} - gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - t_param=t_param, - hamiltonian_value_dict=value_dict, - gradient_params=[t_param, theta1], - ) - - # expected_gradient = {t_param: (4.7628567756419216e-12+0j)} - # np.testing.assert_equal(gradient, expected_gradient) - print(gradient) + # TODO this test is disabled for the time being - no t_param for TrotterQrte + # def test_trotter_qrte_gradient_summed_op_qdrift_t_param_grad(self): + # """Test for trotter qrte gradient with SummedOp and QDrift; gradient not on all + # parameters.""" + # t_param = Parameter("t_param") + # theta1 = Parameter("theta1") + # operator = t_param * (I ^ Z ^ I) + theta1 * (Z ^ I ^ I) + # trotter_qrte = TrotterQrte(QDrift()) + # initial_state = Zero + # time = 5 + # gradient_object = None + # observable = Z + # value_dict = {theta1: 2, t_param: 5} + # gradient = trotter_qrte.gradient( + # operator, + # time, + # initial_state, + # gradient_object, + # observable, + # t_param=t_param, + # hamiltonian_value_dict=value_dict, + # gradient_params=[t_param], + # ) + # + # expected_gradient = {t_param: (4.729550084903167e-12 + 0j)} + # np.testing.assert_equal(gradient, expected_gradient) + # + # TODO this test is disabled for the time being - no t_param for TrotterQrte + # def test_trotter_qrte_gradient_summed_op_qdrift_t_param_theta(self): + # """Test for trotter qrte gradient with SummedOp and QDrift; gradient on t_param and + # another parameter.""" + # t_param = Parameter("t_param") + # theta1 = Parameter("theta1") + # operator = t_param * (I ^ Z ^ I) + theta1 * (Z ^ I ^ I) + # mode = TrotterModeEnum.QDRIFT + # trotter_qrte = TrotterQrte(mode) + # initial_state = Zero + # time = 5 + # gradient_object = None + # observable = Z + # value_dict = {theta1: 2, t_param: 5} + # gradient = trotter_qrte.gradient( + # operator, + # time, + # initial_state, + # gradient_object, + # observable, + # t_param=t_param, + # hamiltonian_value_dict=value_dict, + # gradient_params=[t_param, theta1], + # ) + # + # # expected_gradient = {t_param: (4.7628567756419216e-12+0j)} + # # np.testing.assert_equal(gradient, expected_gradient) + # print(gradient) if __name__ == "__main__": From 0312a4db1e3ea6ee325a489d882d0e59691d7850 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 8 Dec 2021 14:19:05 +0100 Subject: [PATCH 015/145] Removed irrelevant files. --- .../builders/__init__.py | 11 - .../builders/evolution_op_builder.py | 20 -- .../builders/implementations/__init__.py | 11 - .../circuit_evolution_op_builder.py | 18 -- .../exact_evolution_op_builder.py | 18 -- .../pauli_trotter_evolution_op_builder.py | 208 ------------------ .../trotterizations/__init__.py | 11 - .../implementations/trotterizations/qdrift.py | 80 ------- .../implementations/trotterizations/suzuki.py | 117 ---------- .../trotterizations/trotter.py | 29 --- .../trotterizations/trotter_mode_enum.py | 18 -- .../trotterizations/trotterization_base.py | 60 ----- .../trotterizations/trotterization_factory.py | 53 ----- .../imaginary/implementations/numpy_qite.py | 16 -- .../imaginary/implementations/var_qite.py | 17 -- .../real/implementations/numpy_qrte.py | 16 -- .../real/implementations/projected_vqd.py | 16 -- .../real/implementations/var_qrte.py | 44 ---- .../results/__init__.py | 11 - .../results/evolution_gradient_result.py | 13 -- .../results/evolution_result.py | 13 -- .../variational/__init__.py | 11 - .../variational/principles/__init__.py | 11 - .../principles/imaginary/__init__.py | 11 - .../imaginary_variational_principle.py | 18 -- .../imaginary/implementations/__init__.py | 11 - .../variational/principles/real/__init__.py | 11 - .../real/implementations/__init__.py | 11 - .../real/real_variational_principle.py | 18 -- .../principles/variational_principle.py | 16 -- .../variational/var_qte.py | 16 -- 31 files changed, 934 deletions(-) delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/pauli_trotter_evolution_op_builder.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter_mode_enum.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/imaginary/implementations/numpy_qite.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/imaginary/implementations/var_qite.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/real/implementations/numpy_qrte.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/real/implementations/projected_vqd.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/results/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/results/evolution_result.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/variational/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/implementations/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/real/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/real/implementations/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/variational/principles/variational_principle.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/variational/var_qte.py diff --git a/qiskit/algorithms/quantum_time_evolution/builders/__init__.py b/qiskit/algorithms/quantum_time_evolution/builders/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py deleted file mode 100644 index 5a8e965c98cd..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/evolution_op_builder.py +++ /dev/null @@ -1,20 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from abc import ABC, abstractmethod - -from qiskit.opflow import OperatorBase - - -class EvolutionOpBuilder(ABC): - @abstractmethod - def build(self, operator: OperatorBase) -> OperatorBase: - raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/__init__.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py deleted file mode 100644 index 3b05cd355b73..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/circuit_evolution_op_builder.py +++ /dev/null @@ -1,18 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import ( - EvolutionOpBuilder, -) - - -class CircuitEvolutionOpBuilder(EvolutionOpBuilder): - pass diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py deleted file mode 100644 index 69018fa10ef6..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/exact_evolution_op_builder.py +++ /dev/null @@ -1,18 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import ( - EvolutionOpBuilder, -) - - -class ExactEvolutionOpBuilder(EvolutionOpBuilder): - pass diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/pauli_trotter_evolution_op_builder.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/pauli_trotter_evolution_op_builder.py deleted file mode 100644 index 7639680ba136..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/pauli_trotter_evolution_op_builder.py +++ /dev/null @@ -1,208 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import ( - EvolutionOpBuilder, -) - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# 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. -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( - TrotterModeEnum, -) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_base import ( - TrotterizationBase, -) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_factory import ( - TrotterizationFactory, -) - -""" PauliTrotterEvolution Class """ - -import logging -from typing import Optional, Union, cast - -import numpy as np - -from qiskit.opflow.converters.pauli_basis_change import PauliBasisChange -from qiskit.opflow.evolutions.evolved_op import EvolvedOp -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.operator_globals import I, Z -from qiskit.opflow.primitive_ops.pauli_op import PauliOp -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp - -# TODO uncomment when we implement Abelian grouped evolution. -# from qiskit.opflow.converters.abelian_grouper import AbelianGrouper - -logger = logging.getLogger(__name__) - - -class PauliTrotterEvolutionOpBuilder(EvolutionOpBuilder): - r""" - An Evolution algorithm replacing exponentiated sums of Paulis by changing them each to the - Z basis, rotating with an rZ, changing back, and Trotterizing. - - More specifically, we compute basis change circuits for each Pauli into a single-qubit Z, - evolve the Z by the desired evolution time with an rZ gate, and change the basis back using - the adjoint of the original basis change circuit. For sums of Paulis, the individual Pauli - evolution circuits are composed together by Trotterization scheme. - """ - - def __init__( - self, - trotter_mode: Optional[ - Union[TrotterModeEnum, TrotterizationBase] - ] = TrotterModeEnum.TROTTER, - reps: Optional[int] = 1, - # TODO uncomment when we implement Abelian grouped evolution. - # group_paulis: Optional[bool] = False - ) -> None: - """ - Args: - trotter_mode: A string ('trotter', 'suzuki', or 'qdrift') to pass to the - TrotterizationFactory, or a TrotterizationBase, indicating how to combine - individual Pauli evolution circuits to equal the exponentiation of the Pauli sum. - reps: How many Trotterization repetitions to make, to improve the approximation - accuracy. - # TODO uncomment when we implement Abelian grouped evolution. - # group_paulis: Whether to group Pauli sums into Abelian - # sub-groups, so a single diagonalization circuit can be used for each group - # rather than each Pauli. - """ - - if isinstance(trotter_mode, TrotterizationBase): - self._trotter = trotter_mode - else: - self._trotter = TrotterizationFactory.build(mode=trotter_mode, reps=reps) - - # TODO uncomment when we implement Abelian grouped evolution. - # self._grouper = AbelianGrouper() if group_paulis else None - - @property - def trotter(self) -> TrotterizationBase: - """TrotterizationBase used to evolve SummedOps.""" - return self._trotter - - @trotter.setter - def trotter(self, trotter: TrotterizationBase) -> None: - """Set TrotterizationBase used to evolve SummedOps.""" - self._trotter = trotter - - def build(self, operator: OperatorBase) -> OperatorBase: - r""" - Traverse the operator, replacing ``EvolvedOps`` with ``CircuitOps`` containing - Trotterized evolutions equalling the exponentiation of -i * operator. - - Args: - operator: The Operator to be trotterized. - - Returns: - The trotterized operator. - """ - # TODO uncomment when we implement Abelian grouped evolution. - # if self._grouper: - # # Sort into commuting groups - # operator = self._grouper.convert(operator).reduce() - return self._recursive_convert(operator) - - def _recursive_convert(self, operator: OperatorBase) -> OperatorBase: - if isinstance(operator, EvolvedOp): - if isinstance(operator.primitive, PauliSumOp): - operator = EvolvedOp(operator.primitive.to_pauli_op(), coeff=operator.coeff) - if not {"Pauli"} == operator.primitive_strings(): - logger.warning( - "Evolved Hamiltonian is not composed of only Paulis, converting to " - "Pauli representation, which can be expensive." - ) - # Setting massive=False because this conversion is implicit. User can perform this - # action on the Hamiltonian with massive=True explicitly if they so choose. - # TODO explore performance to see whether we should avoid doing this repeatedly - pauli_ham = operator.primitive.to_pauli_op(massive=False) - operator = EvolvedOp(pauli_ham, coeff=operator.coeff) - - if isinstance(operator.primitive, SummedOp): - # TODO uncomment when we implement Abelian grouped evolution. - # if operator.primitive.abelian: - # return self.evolution_for_abelian_paulisum(operator.primitive) - # else: - # Collect terms that are not the identity. - oplist = [ - x - for x in operator.primitive - if not isinstance(x, PauliOp) or sum(x.primitive.x + x.primitive.z) != 0 - ] - # Collect the coefficients of any identity terms, - # which become global phases when exponentiated. - identity_phases = [ - x.coeff - for x in operator.primitive - if isinstance(x, PauliOp) and sum(x.primitive.x + x.primitive.z) == 0 - ] - # Construct sum without the identity operators. - new_primitive = SummedOp(oplist, coeff=operator.primitive.coeff) - trotterized = self.trotter.build(new_primitive) - circuit_no_identities = self._recursive_convert(trotterized) - # Set the global phase of the QuantumCircuit to account for removed identity terms. - global_phase = -sum(identity_phases) * operator.primitive.coeff - circuit_no_identities.primitive.global_phase = global_phase - return circuit_no_identities - elif isinstance(operator.primitive, PauliOp): - return self.evolution_for_pauli(operator.primitive) - # Covers ListOp, ComposedOp, TensoredOp - elif isinstance(operator.primitive, ListOp): - converted_ops = [self._recursive_convert(op) for op in operator.primitive.oplist] - return operator.primitive.__class__(converted_ops, coeff=operator.coeff) - elif isinstance(operator, ListOp): - return operator.traverse(self.build).reduce() - - return operator - - def evolution_for_pauli(self, pauli_op: PauliOp) -> PrimitiveOp: - r""" - Compute evolution Operator for a single Pauli using a ``PauliBasisChange``. - - Args: - pauli_op: The ``PauliOp`` to evolve. - - Returns: - A ``PrimitiveOp``, either the evolution ``CircuitOp`` or a ``PauliOp`` equal to the - identity if pauli_op is the identity. - """ - - def replacement_fn(cob_instr_op, dest_pauli_op): - z_evolution = dest_pauli_op.exp_i() - # Remember, circuit composition order is mirrored operator composition order. - return cob_instr_op.adjoint().compose(z_evolution).compose(cob_instr_op) - - # Note: PauliBasisChange will pad destination with identities - # to produce correct CoB circuit - sig_bits = np.logical_or(pauli_op.primitive.z, pauli_op.primitive.x) - a_sig_bit = int(max(np.extract(sig_bits, np.arange(pauli_op.num_qubits)[::-1]))) - destination = (I.tensorpower(a_sig_bit)) ^ (Z * pauli_op.coeff) - cob = PauliBasisChange(destination_basis=destination, replacement_fn=replacement_fn) - return cast(PrimitiveOp, cob.convert(pauli_op)) - - # TODO implement Abelian grouped evolution. - def evolution_for_abelian_paulisum(self, op_sum: SummedOp) -> PrimitiveOp: - """Evolution for abelian pauli sum""" - raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py deleted file mode 100644 index a73e8ccd8ab0..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/qdrift.py +++ /dev/null @@ -1,80 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -""" -QDrift Class - -""" - -from typing import List, Union, cast - -import numpy as np - -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_base import ( - TrotterizationBase, -) -from qiskit.opflow.list_ops.composed_op import ComposedOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp -from qiskit.utils import algorithm_globals - -# pylint: disable=invalid-name - - -class QDrift(TrotterizationBase): - """The QDrift Trotterization method, which selects each each term in the - Trotterization randomly, with a probability proportional to its weight. Based on the work - of Earl Campbell in https://arxiv.org/abs/1811.08017. - """ - - def __init__(self, reps: int = 1) -> None: - r""" - Args: - reps: The number of times to repeat the Trotterization circuit. - """ - super().__init__(reps=reps) - - def build(self, operator: OperatorBase) -> OperatorBase: - if not isinstance(operator, (SummedOp, PauliSumOp)): - raise TypeError("Trotterization converters can only convert SummedOp or PauliSumOp.") - - if not isinstance(operator.coeff, (float, int)): - raise TypeError( - "Trotterization converters can only convert operators with real coefficients." - ) - - operator_iter: Union[PauliSumOp, List[PrimitiveOp]] - - if isinstance(operator, PauliSumOp): - operator_iter = operator - coeffs = operator.primitive.coeffs - coeff = operator.coeff - else: - operator_iter = cast(List[PrimitiveOp], operator.oplist) - coeffs = [op.coeff for op in operator_iter] - coeff = operator.coeff - - # We artificially make the weights positive, TODO check approximation performance - weights = np.abs(coeffs) - lambd = np.sum(weights) - - N = 2 * (lambd ** 2) * (coeff ** 2) - factor = lambd * coeff / (N * self.reps) - # The protocol calls for the removal of the individual coefficients, - # and multiplication by a constant factor. - scaled_ops = [(op * (factor / op.coeff)).exp_i() for op in operator_iter] - sampled_ops = algorithm_globals.random.choice( - scaled_ops, size=(int(N * self.reps),), p=weights / lambd - ) - - return ComposedOp(sampled_ops).reduce() diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py deleted file mode 100644 index 6da359ad7970..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/suzuki.py +++ /dev/null @@ -1,117 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -""" Suzuki Class """ - -from typing import List, Union, cast - -from numpy import isreal - -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_base import ( - TrotterizationBase, -) -from qiskit.circuit import ParameterExpression -from qiskit.opflow.list_ops.composed_op import ComposedOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp - - -class Suzuki(TrotterizationBase): - r""" - Suzuki Trotter expansion, composing the evolution circuits of each Operator in the sum - together by a recursive "bookends" strategy, repeating the whole composed circuit - ``reps`` times. - - Detailed in https://arxiv.org/pdf/quant-ph/0508139.pdf. - """ - - def __init__(self, reps: int = 1, order: int = 2) -> None: - """ - Args: - reps: The number of times to repeat the expansion circuit. - order: The order of the expansion to perform. - - """ - super().__init__(reps=reps) - self._order = order - - @property - def order(self) -> int: - """returns order""" - return self._order - - @order.setter - def order(self, order: int) -> None: - """sets order""" - self._order = order - - def build(self, operator: OperatorBase) -> OperatorBase: - if not isinstance(operator, (SummedOp, PauliSumOp)): - raise TypeError("Trotterization converters can only convert SummedOp or PauliSumOp.") - - if isinstance(operator.coeff, (float, ParameterExpression)): - coeff = operator.coeff - else: - if isreal(operator.coeff): - coeff = operator.coeff.real - else: - raise TypeError( - "Coefficient of the operator must be float or ParameterExpression, " - f"but {operator.coeff}:{type(operator.coeff)} is given." - ) - - if isinstance(operator, PauliSumOp): - comp_list = self._recursive_expansion(operator, coeff, self.order, self.reps) - if isinstance(operator, SummedOp): - comp_list = Suzuki._recursive_expansion(operator.oplist, coeff, self.order, self.reps) - - single_rep = ComposedOp(cast(List[OperatorBase], comp_list)) - full_evo = single_rep.power(self.reps) - return full_evo.reduce() - - @staticmethod - def _recursive_expansion( - op_list: Union[List[OperatorBase], PauliSumOp], - evo_time: Union[float, ParameterExpression], - expansion_order: int, - reps: int, - ) -> List[PrimitiveOp]: - """ - Compute the list of pauli terms for a single slice of the Suzuki expansion - following the paper https://arxiv.org/pdf/quant-ph/0508139.pdf. - - Args: - op_list: The slice's weighted Pauli list for the Suzuki expansion - evo_time: The parameter lambda as defined in said paper, - adjusted for the evolution time and the number of time slices - expansion_order: The order for the Suzuki expansion. - reps: The number of times to repeat the expansion circuit. - - Returns: - The evolution list after expansion. - """ - if expansion_order == 1: - # Base first-order Trotter case - return [(op * (evo_time / reps)).exp_i() for op in op_list] # type: ignore - if expansion_order == 2: - half = Suzuki._recursive_expansion(op_list, evo_time / 2, expansion_order - 1, reps) - return list(reversed(half)) + half - else: - p_k = (4 - 4 ** (1 / (2 * expansion_order - 1))) ** -1 - side = 2 * Suzuki._recursive_expansion( - op_list, evo_time * p_k, expansion_order - 2, reps - ) - middle = Suzuki._recursive_expansion( - op_list, evo_time * (1 - 4 * p_k), expansion_order - 2, reps - ) - return side + middle + side diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py deleted file mode 100644 index b4f4331adc05..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter.py +++ /dev/null @@ -1,29 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -""" Trotter Class """ -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki import ( - Suzuki, -) - - -class Trotter(Suzuki): - r""" - Simple Trotter expansion, composing the evolution circuits of each Operator in the sum - together ``reps`` times and dividing the evolution time of each by ``reps``. - """ - - def __init__(self, reps: int = 1) -> None: - r""" - Args: - reps: The number of times to repeat the Trotterization circuit. - """ - super().__init__(order=1, reps=reps) diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter_mode_enum.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter_mode_enum.py deleted file mode 100644 index b6b80ca62ca6..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotter_mode_enum.py +++ /dev/null @@ -1,18 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from enum import Enum - - -class TrotterModeEnum(Enum): - TROTTER = "trotter" - SUZUKI = "suzuki" - QDRIFT = "qdrift" diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py deleted file mode 100644 index 43ff3c17e093..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_base.py +++ /dev/null @@ -1,60 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. - -""" Trotterization Algorithm Base """ - -from abc import abstractmethod - -from qiskit.algorithms.quantum_time_evolution.builders.evolution_op_builder import ( - EvolutionOpBuilder, -) -from qiskit.opflow.operator_base import OperatorBase - -# TODO centralize handling of commuting groups - - -class TrotterizationBase(EvolutionOpBuilder): - """A base for Trotterization methods, algorithms for approximating exponentiations of - operator sums by compositions of exponentiations. - """ - - def __init__(self, reps: int = 1) -> None: - - self._reps = reps - - @property - def reps(self) -> int: - """The number of repetitions to use in the Trotterization, improving the approximation - accuracy. - """ - return self._reps - - @reps.setter - def reps(self, reps: int) -> None: - r"""Set the number of repetitions to use in the Trotterization.""" - self._reps = reps - - @abstractmethod - def build(self, operator: OperatorBase) -> OperatorBase: - r""" - Convert a ``SummedOp`` into a ``ComposedOp`` or ``CircuitOp`` representing an - approximation of e^-i*``op_sum``. - Args: - operator: The ``SummedOp`` to evolve. - Returns: - The Operator approximating op_sum's evolution. - Raises: - TypeError: A non-SummedOps Operator is passed into ``convert``. - """ - raise NotImplementedError - - # TODO @abstractmethod - trotter_error_bound diff --git a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py b/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py deleted file mode 100644 index 1e56f38b3efb..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/builders/implementations/trotterizations/trotterization_factory.py +++ /dev/null @@ -1,53 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -"""TrotterizationFactory Class """ -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.qdrift import ( - QDrift, -) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki import ( - Suzuki, -) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter import ( - Trotter, -) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( - TrotterModeEnum, -) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotterization_base import ( - TrotterizationBase, -) - - -class TrotterizationFactory: - """A factory for conveniently creating TrotterizationBase instances.""" - - @staticmethod - def build(mode: TrotterModeEnum = TrotterModeEnum.TROTTER, reps: int = 1) -> TrotterizationBase: - """A factory for conveniently creating TrotterizationBase instances. - Args: - mode: One of 'trotter', 'suzuki', 'qdrift' - reps: The number of times to repeat the Trotterization circuit. - Returns: - The desired TrotterizationBase instance. - Raises: - ValueError: A string not in ['trotter', 'suzuki', 'qdrift'] is given for mode. - """ - if mode == TrotterModeEnum.TROTTER: - return Trotter(reps=reps) - - elif mode == TrotterModeEnum.SUZUKI: - return Suzuki(reps=reps) - - elif mode == TrotterModeEnum.QDRIFT: - return QDrift(reps=reps) - - raise ValueError(f"Trotter mode {mode.value} not supported") diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/numpy_qite.py b/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/numpy_qite.py deleted file mode 100644 index c6e1f1fd79ae..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/numpy_qite.py +++ /dev/null @@ -1,16 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from qiskit.algorithms.quantum_time_evolution.imaginary.qite import Qite - - -class NumPyQite(Qite): - pass diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/var_qite.py b/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/var_qite.py deleted file mode 100644 index d62128052fc7..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/var_qite.py +++ /dev/null @@ -1,17 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from qiskit.algorithms.quantum_time_evolution.imaginary.qite import Qite -from qiskit.algorithms.quantum_time_evolution.variational.var_qte import VarQte - - -class VarQite(Qite, VarQte): - pass diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/numpy_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/numpy_qrte.py deleted file mode 100644 index 9670220a5091..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/numpy_qrte.py +++ /dev/null @@ -1,16 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte - - -class NumPyQrte(Qrte): - pass diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/projected_vqd.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/projected_vqd.py deleted file mode 100644 index baf08aef1c6b..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/projected_vqd.py +++ /dev/null @@ -1,16 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte - - -class ProjectedVqd(Qrte): - pass diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py deleted file mode 100644 index 2684f846f649..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/var_qrte.py +++ /dev/null @@ -1,44 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte -from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import ( - EvolutionGradientResult, -) -from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult -from qiskit.algorithms.quantum_time_evolution.variational.var_qte import VarQte -from qiskit.opflow import OperatorBase, StateFn, Gradient - - -class VarQrte(Qrte, VarQte): - def evolve( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn = None, - observable: OperatorBase = None, - t_param=None, - hamiltonian_value_dict=None, - ) -> EvolutionResult: - raise NotImplementedError() - - def gradient( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn, - gradient_object: Gradient, - observable: OperatorBase = None, - t_param=None, - hamiltonian_value_dict=None, - gradient_params=None, - ) -> EvolutionGradientResult: - raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/results/__init__.py b/qiskit/algorithms/quantum_time_evolution/results/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/results/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py b/qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py deleted file mode 100644 index 314fd52831d4..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/results/evolution_gradient_result.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -class EvolutionGradientResult: - pass diff --git a/qiskit/algorithms/quantum_time_evolution/results/evolution_result.py b/qiskit/algorithms/quantum_time_evolution/results/evolution_result.py deleted file mode 100644 index 146f5207703a..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/results/evolution_result.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -class EvolutionResult: - pass diff --git a/qiskit/algorithms/quantum_time_evolution/variational/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/variational/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/variational/principles/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py deleted file mode 100644 index c2d24e023a0c..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/imaginary_variational_principle.py +++ /dev/null @@ -1,18 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from qiskit.algorithms.quantum_time_evolution.variational.principles.variational_principle import ( - VariationalPrinciple, -) - - -class ImaginaryVariationalPrinciple(VariationalPrinciple): - pass diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/implementations/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/implementations/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/variational/principles/imaginary/implementations/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/real/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/real/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/variational/principles/real/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/real/implementations/__init__.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/real/implementations/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/variational/principles/real/implementations/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py deleted file mode 100644 index 96d37f7d2389..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/variational/principles/real/real_variational_principle.py +++ /dev/null @@ -1,18 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from qiskit.algorithms.quantum_time_evolution.variational.principles.variational_principle import ( - VariationalPrinciple, -) - - -class RealVariationalPrinciple(VariationalPrinciple): - pass diff --git a/qiskit/algorithms/quantum_time_evolution/variational/principles/variational_principle.py b/qiskit/algorithms/quantum_time_evolution/variational/principles/variational_principle.py deleted file mode 100644 index 56f32cf0a503..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/variational/principles/variational_principle.py +++ /dev/null @@ -1,16 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from abc import ABC - - -class VariationalPrinciple(ABC): - pass diff --git a/qiskit/algorithms/quantum_time_evolution/variational/var_qte.py b/qiskit/algorithms/quantum_time_evolution/variational/var_qte.py deleted file mode 100644 index a89947ed09a7..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/variational/var_qte.py +++ /dev/null @@ -1,16 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -from abc import ABC - - -class VarQte(ABC): - pass From e1a3b1b0d2a14247d4d979e5d7f9798c14d2d31d Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 8 Dec 2021 14:19:59 +0100 Subject: [PATCH 016/145] Removed irrelevant files. --- .../quantum_time_evolution/evolution_base.py | 8 ++------ .../trotterization/trotter_qrte.py | 16 ++++++---------- .../quantum_time_evolution/real/qrte.py | 8 ++------ 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/evolution_base.py b/qiskit/algorithms/quantum_time_evolution/evolution_base.py index 041ea495303b..d1eb920a6cf2 100644 --- a/qiskit/algorithms/quantum_time_evolution/evolution_base.py +++ b/qiskit/algorithms/quantum_time_evolution/evolution_base.py @@ -11,10 +11,6 @@ # that they have been altered from the originals. from abc import ABC, abstractmethod -from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import ( - EvolutionGradientResult, -) -from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult from qiskit.opflow import OperatorBase, StateFn, Gradient @@ -28,7 +24,7 @@ def evolve( observable: OperatorBase = None, t_param=None, hamiltonian_value_dict=None, - ) -> EvolutionResult: + ): raise NotImplementedError() @abstractmethod @@ -42,5 +38,5 @@ def gradient( t_param=None, hamiltonian_value_dict=None, gradient_params=None, - ) -> EvolutionGradientResult: + ): raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index fdc0d77439c3..c95c049fb70b 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -17,10 +17,6 @@ from typing import Union, Optional from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte -from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import ( - EvolutionGradientResult, -) -from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult from qiskit.circuit import Parameter, ParameterExpression from qiskit.opflow import OperatorBase, StateFn, Gradient, commutator, SummedOp, PauliSumOp, \ PauliOp, CircuitOp @@ -60,8 +56,8 @@ def evolve( initial_state: StateFn = None, observable: OperatorBase = None, t_param: Parameter = None, - hamiltonian_value_dict=None, - ) -> EvolutionResult: + hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, + ): """ Args: hamiltonian (Pauli | PauliOp | SparsePauliOp | PauliSumOp | list): @@ -141,10 +137,10 @@ def gradient( initial_state: StateFn, gradient_object: Optional[Gradient], observable: OperatorBase = None, - t_param=None, - hamiltonian_value_dict=None, - gradient_params=None, - ) -> EvolutionGradientResult: + t_param: Parameter = None, + hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, + gradient_params: List[Parameter] = None, + ): if observable is None: raise NotImplementedError( "Observable not provided. Probability gradients are not yet supported by " diff --git a/qiskit/algorithms/quantum_time_evolution/real/qrte.py b/qiskit/algorithms/quantum_time_evolution/real/qrte.py index 8ebead4e2434..909ccf702f26 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/qrte.py @@ -12,10 +12,6 @@ from abc import abstractmethod from qiskit.algorithms.quantum_time_evolution.evolution_base import EvolutionBase -from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import ( - EvolutionGradientResult, -) -from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult from qiskit.opflow import StateFn, OperatorBase, Gradient @@ -29,7 +25,7 @@ def evolve( observable: OperatorBase = None, t_param=None, hamiltonian_value_dict=None, - ) -> EvolutionResult: + ): raise NotImplementedError() @abstractmethod @@ -43,5 +39,5 @@ def gradient( t_param=None, hamiltonian_value_dict=None, gradient_params=None, - ) -> EvolutionGradientResult: + ): raise NotImplementedError() From 3f7bb8cb87506fa571586646baeceb9269e610cd Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 8 Dec 2021 14:22:32 +0100 Subject: [PATCH 017/145] Extended docs. --- .../trotterization/trotter_qrte.py | 77 ++++++++++++------- qiskit/test/base.py | 1 + .../trotterization/test_trotter_qrte.py | 23 ++++-- 3 files changed, 66 insertions(+), 35 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index c95c049fb70b..f43d2406437f 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -14,18 +14,26 @@ import numbers from collections import defaultdict -from typing import Union, Optional +from typing import Union, Optional, List from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte from qiskit.circuit import Parameter, ParameterExpression -from qiskit.opflow import OperatorBase, StateFn, Gradient, commutator, SummedOp, PauliSumOp, \ - PauliOp, CircuitOp +from qiskit.opflow import ( + OperatorBase, + StateFn, + Gradient, + commutator, + SummedOp, + PauliSumOp, + PauliOp, + CircuitOp, +) from qiskit.circuit.library import PauliEvolutionGate from qiskit.synthesis import ProductFormula, LieTrotter class TrotterQrte(Qrte): - """ TODO write documentation + """ Class for performing Quantum Real Time Evolution using Trotterization. Type of Troterrization is defined by a ProductFormula provided. Examples: @@ -41,11 +49,12 @@ class TrotterQrte(Qrte): initial_state = Zero evolved_state = trotter_qrte.evolve(operator, 1, initial_state) """ + def __init__(self, product_formula: ProductFormula = LieTrotter()): """ Args: product_formula: A Lie-Trotter-Suzuki product formula. The default is the Lie-Trotter - first order product formula with a single repetition. + first order product formula with a single repetition. """ self.product_formula = product_formula @@ -64,11 +73,12 @@ def evolve( The operator to evolve. Can also be provided as list of non-commuting operators where the elements are sums of commuting operators. For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. - time: The evolution time. - initial_state: TODO. - observable: TODO. - t_param: Not accepted in this class. - hamiltonian_value_dict: TODO + time: Total time of evolution. + initial_state: If interested in a quantum state time evolution, a quantum state to be evolved. + observable: If interested in a quantum observable time evolution, a quantum observable to be evolved. + t_param: Not supported by this algorithm. + hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to + certain values. Returns: The evolved hamiltonian applied to either an initial state or an observable. @@ -77,27 +87,31 @@ def evolve( ValueError: If t_param is not set to None (feature not currently supported). """ if t_param is not None: - raise ValueError("TrotterQrte does not accept a time dependent hamiltonian," - "t_param should be set to None.") + raise ValueError( + "TrotterQrte does not accept a time dependent hamiltonian," + "t_param should be set to None." + ) hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) self._validate_input(initial_state, observable) # the evolution gate - evolution_gate = CircuitOp(PauliEvolutionGate(hamiltonian, time, - synthesis=self.product_formula)) + evolution_gate = CircuitOp( + PauliEvolutionGate(hamiltonian, time, synthesis=self.product_formula) + ) if initial_state is not None: return (evolution_gate @ initial_state).eval() if observable is not None: # TODO Temporary patch due to terra bug - evolution_gate_adjoint = CircuitOp(PauliEvolutionGate(hamiltonian[::-1], -time, - synthesis=self.product_formula)) + evolution_gate_adjoint = CircuitOp( + PauliEvolutionGate(hamiltonian[::-1], -time, synthesis=self.product_formula) + ) # return evolution_gate.adjoint() @ observable @ evolution_gate return evolution_gate_adjoint @ observable @ evolution_gate def _try_binding_params(self, hamiltonian, hamiltonian_value_dict): # PauliSumOp does not allow parametrized coefficients but after binding the parameters - # we need to convert it into a PauliSumOp for the PauliEvolutionGate + # we need to convert it into a PauliSumOp for the PauliEvolutionGate. if isinstance(hamiltonian, SummedOp): op_list = [] for op in hamiltonian.oplist: @@ -105,19 +119,28 @@ def _try_binding_params(self, hamiltonian, hamiltonian_value_dict): op_bound = op.bind_parameters(hamiltonian_value_dict) else: op_bound = op - if len(op_bound.parameters) > 0: - raise ValueError( - f"Did not manage to bind all parameters in the Hamiltonian, " - f"these parameters encountered: {op_bound.parameters}." - ) + self._is_op_bound(op_bound) op_list.append(op_bound) return sum(op_list) - # for an observable, we might have an OperatorBase... TODO - elif isinstance(hamiltonian, PauliOp): - return hamiltonian.bind_parameters(hamiltonian_value_dict) + elif isinstance(hamiltonian, PauliOp) or isinstance(hamiltonian, OperatorBase): + if hamiltonian_value_dict is not None: + op_bound = hamiltonian.bind_parameters(hamiltonian_value_dict) + else: + op_bound = hamiltonian + + self._is_op_bound(op_bound) + return op_bound else: + self._is_op_bound(hamiltonian) return hamiltonian + def _is_op_bound(self, op_bound): + if len(op_bound.parameters) > 0: + raise ValueError( + f"Did not manage to bind all parameters in the Hamiltonian, " + f"these parameters encountered: {op_bound.parameters}." + ) + def _validate_input(self, initial_state, observable): if initial_state is None and observable is None: raise ValueError( @@ -132,10 +155,10 @@ def _validate_input(self, initial_state, observable): def gradient( self, - hamiltonian: Union[SummedOp], + hamiltonian: OperatorBase, time: float, initial_state: StateFn, - gradient_object: Optional[Gradient], + gradient_object: Gradient, observable: OperatorBase = None, t_param: Parameter = None, hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, diff --git a/qiskit/test/base.py b/qiskit/test/base.py index 7cbd80d73431..e6814a89d1e7 100644 --- a/qiskit/test/base.py +++ b/qiskit/test/base.py @@ -62,6 +62,7 @@ class BaseTestCase(testtools.TestCase): assertRaises = unittest.TestCase.assertRaises assertEqual = unittest.TestCase.assertEqual + else: class BaseTestCase(unittest.TestCase): diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 47541ff89a35..afff9367ea3a 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -19,8 +19,9 @@ from numpy.testing import assert_raises from scipy.linalg import expm -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte \ - import TrotterQrte +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( + TrotterQrte, +) from qiskit.quantum_info import Statevector from qiskit.utils import algorithm_globals from qiskit.circuit import Parameter @@ -48,8 +49,9 @@ def test_trotter_qrte_trotter(self): initial_state = Zero evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state - expected_state = expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ \ - initial_state.to_matrix() + expected_state = ( + expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() + ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) np.testing.assert_equal(evolved_state, expected_evolved_state) @@ -79,8 +81,9 @@ def test_trotter_qrte_trotter_observable(self): evolved_observable = evolved_observable.to_matrix_op() # Calculate the expected operator expected_op = expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) - expected_evolved_observable = MatrixOp(expected_op.conj().T @ observable.to_matrix() - @ expected_op) + expected_evolved_observable = MatrixOp( + expected_op.conj().T @ observable.to_matrix() @ expected_op + ) np.testing.assert_equal(evolved_observable, expected_evolved_observable) @@ -92,8 +95,12 @@ def test_trotter_qrte_suzuki(self): initial_state = Zero evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state - expected_state = expm(-1j * X.to_matrix() * 0.5) @ expm(-1j * Z.to_matrix()) \ - @ expm(-1j * X.to_matrix() * 0.5) @ initial_state.to_matrix() + expected_state = ( + expm(-1j * X.to_matrix() * 0.5) + @ expm(-1j * Z.to_matrix()) + @ expm(-1j * X.to_matrix() * 0.5) + @ initial_state.to_matrix() + ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) np.testing.assert_equal(evolved_state, expected_evolved_state) From 3534d612d8400b2210e85a49081051480b1dfc08 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 8 Dec 2021 14:26:22 +0100 Subject: [PATCH 018/145] Removed outdated unit tests. --- .../trotterization/test_trotter_qrte.py | 77 ------------------- 1 file changed, 77 deletions(-) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index afff9367ea3a..900d3b463a83 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -123,27 +123,6 @@ def test_trotter_qrte_qdrift(self): np.testing.assert_equal(evolved_state, expected_evolved_state) - # TODO this test is disabled for the time being - no t_param for TrotterQrte - # def test_trotter_qrte_trotter_binding(self): - # """Test for trotter qrte with binding.""" - # t_param = Parameter("t") - # operator = X * t_param + Z - # hamiltonian_value_dict = {t_param: 1} - # trotter_qrte = TrotterQrte() - # initial_state = Zero - # evolved_state = trotter_qrte.evolve( - # operator, - # 1, - # initial_state, - # t_param=t_param, - # hamiltonian_value_dict=hamiltonian_value_dict, - # ) - # expected_evolved_state = VectorStateFn( - # Statevector([0.29192658 - 0.45464871j, -0.70807342 - 0.45464871j], dims=(2,)) - # ) - # - # np.testing.assert_equal(evolved_state, expected_evolved_state) - # def test_trotter_qrte_trotter_binding_missing_dict(self): """Test for trotter qrte with binding and missing dictionary..""" t_param = Parameter("t") @@ -286,62 +265,6 @@ def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): expected_gradient = {theta1: 0j} np.testing.assert_equal(gradient, expected_gradient) - # TODO this test is disabled for the time being - no t_param for TrotterQrte - # def test_trotter_qrte_gradient_summed_op_qdrift_t_param_grad(self): - # """Test for trotter qrte gradient with SummedOp and QDrift; gradient not on all - # parameters.""" - # t_param = Parameter("t_param") - # theta1 = Parameter("theta1") - # operator = t_param * (I ^ Z ^ I) + theta1 * (Z ^ I ^ I) - # trotter_qrte = TrotterQrte(QDrift()) - # initial_state = Zero - # time = 5 - # gradient_object = None - # observable = Z - # value_dict = {theta1: 2, t_param: 5} - # gradient = trotter_qrte.gradient( - # operator, - # time, - # initial_state, - # gradient_object, - # observable, - # t_param=t_param, - # hamiltonian_value_dict=value_dict, - # gradient_params=[t_param], - # ) - # - # expected_gradient = {t_param: (4.729550084903167e-12 + 0j)} - # np.testing.assert_equal(gradient, expected_gradient) - # - # TODO this test is disabled for the time being - no t_param for TrotterQrte - # def test_trotter_qrte_gradient_summed_op_qdrift_t_param_theta(self): - # """Test for trotter qrte gradient with SummedOp and QDrift; gradient on t_param and - # another parameter.""" - # t_param = Parameter("t_param") - # theta1 = Parameter("theta1") - # operator = t_param * (I ^ Z ^ I) + theta1 * (Z ^ I ^ I) - # mode = TrotterModeEnum.QDRIFT - # trotter_qrte = TrotterQrte(mode) - # initial_state = Zero - # time = 5 - # gradient_object = None - # observable = Z - # value_dict = {theta1: 2, t_param: 5} - # gradient = trotter_qrte.gradient( - # operator, - # time, - # initial_state, - # gradient_object, - # observable, - # t_param=t_param, - # hamiltonian_value_dict=value_dict, - # gradient_params=[t_param, theta1], - # ) - # - # # expected_gradient = {t_param: (4.7628567756419216e-12+0j)} - # # np.testing.assert_equal(gradient, expected_gradient) - # print(gradient) - if __name__ == "__main__": unittest.main() From 59a8f9afc5c48fc11cb8a0fb56f20ac916245d9a Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 13 Dec 2021 15:32:16 +0100 Subject: [PATCH 019/145] Removed outdated files. --- .../builders/__init__.py | 11 -- .../builders/implementations/__init__.py | 11 -- .../test_trotterization_builder.py | 183 ------------------ .../trotterizations/__init__.py | 11 -- .../trotterizations/test_qdrift.py | 72 ------- .../trotterizations/test_suzuki.py | 44 ----- .../trotterizations/test_trotter.py | 41 ---- .../test_trotterization_factory.py | 11 -- 8 files changed, 384 deletions(-) delete mode 100644 test/python/algorithms/quantum_time_evolution/builders/__init__.py delete mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/__init__.py delete mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py delete mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py delete mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py delete mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py delete mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py delete mode 100644 test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotterization_factory.py diff --git a/test/python/algorithms/quantum_time_evolution/builders/__init__.py b/test/python/algorithms/quantum_time_evolution/builders/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/test/python/algorithms/quantum_time_evolution/builders/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/__init__.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py deleted file mode 100644 index 703e206f2888..000000000000 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/test_trotterization_builder.py +++ /dev/null @@ -1,183 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. - -""" Test Trotterization Evolution Builder. """ -import unittest - -import numpy as np -import scipy.linalg -from qiskit import quantum_info - -from qiskit.algorithms.quantum_time_evolution.builders.implementations.pauli_trotter_evolution_op_builder import ( - PauliTrotterEvolutionOpBuilder, -) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.trotter_mode_enum import ( - TrotterModeEnum, -) -from test.python.opflow import QiskitOpflowTestCase -import qiskit -from qiskit.circuit import ParameterVector -from qiskit.opflow import ( - CX, - EvolvedOp, - H, - I, - ListOp, - X, - Y, - Z, - Zero, -) - - -class TestTrotterizationBuilder(QiskitOpflowTestCase): - """Trotterization Evolution Builder tests.""" - - def test_trotter_with_identity(self): - """trotterization of operator with identity term""" - op = (2.0 * I ^ I) + (Z ^ Y) - exact_matrix = scipy.linalg.expm(-1j * op.to_matrix()) - evo = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.SUZUKI, reps=2) - with self.subTest("all PauliOp terms"): - circ_op = evo.build(EvolvedOp(op)) - circuit_matrix = quantum_info.Operator(circ_op.to_circuit()).data - np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) - - with self.subTest("MatrixOp identity term"): - op = (2.0 * I ^ I).to_matrix_op() + (Z ^ Y) - circ_op = evo.build(EvolvedOp(op)) - circuit_matrix = quantum_info.Operator(circ_op.to_circuit()).data - np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) - - with self.subTest("CircuitOp identity term"): - op = (2.0 * I ^ I).to_circuit_op() + (Z ^ Y) - circ_op = evo.build(EvolvedOp(op)) - circuit_matrix = quantum_info.Operator(circ_op.to_circuit()).data - np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) - - def test_parameterized_evolution(self): - """parameterized evolution test""" - thetas = ParameterVector("θ", length=7) - op = ( - (thetas[0] * I ^ I) - + (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) - ) - op = op * thetas[6] - evolution = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) - # wf = (Pl^Pl) + (Ze^Ze) - wf = (op).exp_i() @ CX @ (H ^ I) @ Zero - mean = evolution.build(wf) - circuit = mean.to_circuit() - # Check that all parameters are in the circuit - for p in thetas: - self.assertIn(p, circuit.parameters) - # Check that the identity-parameters only exist as global phase - self.assertNotIn(thetas[0], circuit._parameter_table.get_keys()) - - def test_bind_parameters(self): - """bind parameters test""" - thetas = ParameterVector("θ", length=6) - op = ( - (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) - ) - op = thetas[0] * op - evolution = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) - # wf = (Pl^Pl) + (Ze^Ze) - wf = (op).exp_i() @ CX @ (H ^ I) @ Zero - wf = wf.assign_parameters({thetas: np.arange(10, 16)}) - mean = evolution.build(wf) - circuit_params = mean.to_circuit().parameters - # Check that the no parameters are in the circuit - for p in thetas[1:]: - self.assertNotIn(p, circuit_params) - - def test_bind_circuit_parameters(self): - """bind circuit parameters test""" - thetas = ParameterVector("θ", length=6) - op = ( - (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) - ) - op = thetas[0] * op - evolution = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) - # wf = (Pl^Pl) + (Ze^Ze) - wf = (op).exp_i() @ CX @ (H ^ I) @ Zero - evo = evolution.build(wf) - mean = evo.assign_parameters({thetas: np.arange(10, 16)}) - # Check that the no parameters are in the circuit - for p in thetas[1:]: - self.assertNotIn(p, mean.to_circuit().parameters) - # Check that original circuit is unchanged - for p in thetas: - self.assertIn(p, evo.to_circuit().parameters) - - # TODO test with other Op types than CircuitStateFn - def test_bind_parameter_list(self): - """bind parameters list test""" - thetas = ParameterVector("θ", length=6) - op = ( - (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) - ) - op = thetas[0] * op - evolution = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) - # wf = (Pl^Pl) + (Ze^Ze) - wf = (op).exp_i() @ CX @ (H ^ I) @ Zero - evo = evolution.build(wf) - param_list = np.transpose([np.arange(10, 16), np.arange(2, 8), np.arange(30, 36)]).tolist() - means = evo.assign_parameters({thetas: param_list}) - self.assertIsInstance(means, ListOp) - # Check that the no parameters are in the circuit - for p in thetas[1:]: - for circop in means.oplist: - self.assertNotIn(p, circop.to_circuit().parameters) - # Check that original circuit is unchanged - for p in thetas: - self.assertIn(p, evo.to_circuit().parameters) - - def test_mixed_evolution(self): - """bind parameters test""" - thetas = ParameterVector("θ", length=6) - op = ( - (thetas[1] * (I ^ Z).to_matrix_op()) - + (thetas[2] * (X ^ X)).to_matrix_op() - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z).to_circuit_op() - + (thetas[5] * (Z ^ I).to_circuit_op()) - ) - op = thetas[0] * op - evolution = PauliTrotterEvolutionOpBuilder(trotter_mode=TrotterModeEnum.TROTTER, reps=1) - wf = (op).exp_i() @ CX @ (H ^ I) @ Zero - wf = wf.assign_parameters({thetas: np.arange(10, 16)}) - mean = evolution.build(wf) - circuit_params = mean.to_circuit().parameters - # Check that the no parameters are in the circuit - for p in thetas[1:]: - self.assertNotIn(p, circuit_params) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py deleted file mode 100644 index 5adea9f03d73..000000000000 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_qdrift.py +++ /dev/null @@ -1,72 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -""" Test QDrift. """ -import unittest - -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.qdrift import ( - QDrift, -) -from test.python.opflow import QiskitOpflowTestCase -from qiskit.opflow import ( - CircuitOp, - EvolvedOp, - I, - SummedOp, - X, - Y, - Z, -) - - -class TestQDrift(QiskitOpflowTestCase): - """QDrift tests.""" - - def test_qdrift(self): - """QDrift test.""" - op = (2 * Z ^ Z) + (3 * X ^ X) - (4 * Y ^ Y) + (0.5 * Z ^ I) - trotterization = QDrift().build(op) - self.assertGreater(len(trotterization.oplist), 150) - last_coeff = None - # Check that all types are correct and all coefficients are equals - for op in trotterization.oplist: - self.assertIsInstance(op, (EvolvedOp, CircuitOp)) - if isinstance(op, EvolvedOp): - if last_coeff: - self.assertEqual(op.primitive.coeff, last_coeff) - else: - last_coeff = op.primitive.coeff - - def test_qdrift_summed_op(self): - """QDrift test for SummedOp.""" - op = SummedOp( - [ - (2 * Z ^ Z), - (3 * X ^ X), - (-4 * Y ^ Y), - (0.5 * Z ^ I), - ] - ) - trotterization = QDrift().build(op) - self.assertGreater(len(trotterization.oplist), 150) - last_coeff = None - # Check that all types are correct and all coefficients are equals - for op in trotterization.oplist: - self.assertIsInstance(op, (EvolvedOp, CircuitOp)) - if isinstance(op, EvolvedOp): - if last_coeff: - self.assertEqual(op.primitive.coeff, last_coeff) - else: - last_coeff = op.primitive.coeff - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py deleted file mode 100644 index 64d0fbc2c498..000000000000 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_suzuki.py +++ /dev/null @@ -1,44 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -""" Test Suzuki. """ -import unittest - -import numpy as np - -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki import ( - Suzuki, -) -from test.python.opflow import QiskitOpflowTestCase -from qiskit.opflow import ( - X, - Z, -) - - -class TestSuzuki(QiskitOpflowTestCase): - """Suzuki tests.""" - - def test_suzuki_directly(self): - """Test for Suzuki converter""" - operator = X + Z - - evo = Suzuki() - evolution = evo.build(operator) - - matrix = np.array( - [[0.29192658 - 0.45464871j, -0.84147098j], [-0.84147098j, 0.29192658 + 0.45464871j]] - ) - np.testing.assert_array_almost_equal(evolution.to_matrix(), matrix) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py deleted file mode 100644 index 9acc5d588e43..000000000000 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotter.py +++ /dev/null @@ -1,41 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -""" Test Trotter. """ -import unittest - -import numpy as np - -from qiskit.algorithms.quantum_time_evolution.builders.implementations.pauli_trotter_evolution_op_builder import ( - PauliTrotterEvolutionOpBuilder, -) -from qiskit.algorithms.quantum_time_evolution.builders.implementations.trotterizations.suzuki import ( - Suzuki, -) -from qiskit.circuit import ParameterVector -from test.python.opflow import QiskitOpflowTestCase -from qiskit.opflow import ( - X, - Z, - I, - Y, - CX, - Zero, - H, -) - - -class TestSuzuki(QiskitOpflowTestCase): - """Trotter tests.""" - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotterization_factory.py b/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotterization_factory.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/test/python/algorithms/quantum_time_evolution/builders/implementations/trotterizations/test_trotterization_factory.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. From 77d541ec6dffab1ab3b1ac32f3c1d8dd8c135e5d Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 13 Dec 2021 15:32:45 +0100 Subject: [PATCH 020/145] Code refactoring, docs extended. --- .../quantum_time_evolution/evolution_base.py | 32 ++++++++-- .../quantum_time_evolution/imaginary/qite.py | 21 ++++--- .../trotterization/trotter_qrte.py | 60 ++++++++++++------- .../quantum_time_evolution/real/qrte.py | 10 ++++ 4 files changed, 89 insertions(+), 34 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/evolution_base.py b/qiskit/algorithms/quantum_time_evolution/evolution_base.py index d1eb920a6cf2..b2f5044f3d99 100644 --- a/qiskit/algorithms/quantum_time_evolution/evolution_base.py +++ b/qiskit/algorithms/quantum_time_evolution/evolution_base.py @@ -9,12 +9,19 @@ # 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. + +"""Base class for quantum time evolution.""" + from abc import ABC, abstractmethod +from typing import Union, List +from qiskit.circuit import Parameter from qiskit.opflow import OperatorBase, StateFn, Gradient class EvolutionBase(ABC): + """Base class for quantum time evolution.""" + @abstractmethod def evolve( self, @@ -22,9 +29,23 @@ def evolve( time: float, initial_state: StateFn = None, observable: OperatorBase = None, - t_param=None, - hamiltonian_value_dict=None, + t_param: Parameter = None, + hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, ): + """ + Evolves an initial state according to a Hamiltonian provided. + Args: + hamiltonian: + ⟨ψ(ω)|H|ψ(ω)〉 + Operator used vor time evolution. + time: Total time of evolution. + initial_state: Quantum state to be evolved. + observable: Observable to be evolved. + t_param: Time parameter in case of a time-dependent Hamiltonian. + hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to + certain values, including the t_param. + # TODO do we allow binding t_param here? + """ raise NotImplementedError() @abstractmethod @@ -35,8 +56,9 @@ def gradient( initial_state: StateFn, gradient_object: Gradient, observable: OperatorBase = None, - t_param=None, - hamiltonian_value_dict=None, - gradient_params=None, + t_param: Parameter = None, + hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, + gradient_params: List[Parameter] = None, ): + """Performs Quantum Time Evolution of gradient expressions.""" raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py b/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py index 4811bcb42153..a9803e16a9c5 100644 --- a/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py +++ b/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py @@ -9,17 +9,18 @@ # 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. + +"""Interface for Quantum Imaginary Time Evolution.""" + from abc import abstractmethod from qiskit.algorithms.quantum_time_evolution.evolution_base import EvolutionBase -from qiskit.algorithms.quantum_time_evolution.results.evolution_gradient_result import ( - EvolutionGradientResult, -) -from qiskit.algorithms.quantum_time_evolution.results.evolution_result import EvolutionResult -from qiskit.opflow import OperatorBase, StateFn +from qiskit.opflow import OperatorBase, StateFn, Gradient class Qite(EvolutionBase): + """Interface for Quantum Imaginary Time Evolution.""" + @abstractmethod def evolve( self, @@ -29,7 +30,11 @@ def evolve( observable: OperatorBase = None, t_param=None, hamiltonian_value_dict=None, - ) -> EvolutionResult: + ): + """ + Performs Quantum Imaginary Time Evolution on an initial state according to a Hamiltonian + provided. + """ raise NotImplementedError() @abstractmethod @@ -38,9 +43,11 @@ def gradient( hamiltonian: OperatorBase, time: float, initial_state: StateFn, + gradient_object: Gradient, observable: OperatorBase = None, t_param=None, hamiltonian_value_dict=None, gradient_params=None, - ) -> EvolutionGradientResult: + ): + """Performs Quantum Imaginary Time Evolution of gradient expressions.""" raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index f43d2406437f..7b1b652991f3 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -14,7 +14,7 @@ import numbers from collections import defaultdict -from typing import Union, Optional, List +from typing import Union, List, Dict from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte from qiskit.circuit import Parameter, ParameterExpression @@ -29,11 +29,13 @@ CircuitOp, ) from qiskit.circuit.library import PauliEvolutionGate +from qiskit.quantum_info import SparsePauliOp, Pauli from qiskit.synthesis import ProductFormula, LieTrotter class TrotterQrte(Qrte): - """ Class for performing Quantum Real Time Evolution using Trotterization. Type of Troterrization is defined by a ProductFormula provided. + """ Class for performing Quantum Real Time Evolution using Trotterization. + Type of Trotterization is defined by a ProductFormula provided. Examples: @@ -60,22 +62,24 @@ def __init__(self, product_formula: ProductFormula = LieTrotter()): def evolve( self, - hamiltonian, + hamiltonian: Union[Pauli, PauliOp, SparsePauliOp, PauliSumOp, List], time: float, initial_state: StateFn = None, observable: OperatorBase = None, t_param: Parameter = None, - hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, - ): + hamiltonian_value_dict: Dict[Parameter, Union[float, complex]] = None, + ) -> StateFn: """ Args: - hamiltonian (Pauli | PauliOp | SparsePauliOp | PauliSumOp | list): + hamiltonian: The operator to evolve. Can also be provided as list of non-commuting operators where the elements are sums of commuting operators. For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. time: Total time of evolution. - initial_state: If interested in a quantum state time evolution, a quantum state to be evolved. - observable: If interested in a quantum observable time evolution, a quantum observable to be evolved. + initial_state: If interested in a quantum state time evolution, a quantum state to be + evolved. + observable: If interested in a quantum observable time evolution, a quantum observable + to be evolved. t_param: Not supported by this algorithm. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to certain values. @@ -101,15 +105,21 @@ def evolve( if initial_state is not None: return (evolution_gate @ initial_state).eval() - if observable is not None: + elif observable is not None: # TODO Temporary patch due to terra bug evolution_gate_adjoint = CircuitOp( PauliEvolutionGate(hamiltonian[::-1], -time, synthesis=self.product_formula) ) - # return evolution_gate.adjoint() @ observable @ evolution_gate return evolution_gate_adjoint @ observable @ evolution_gate + else: + raise ValueError( + "Did not manage to evolve because both initial_state and observable " + "provided are None." + ) - def _try_binding_params(self, hamiltonian, hamiltonian_value_dict): + def _try_binding_params( + self, hamiltonian, hamiltonian_value_dict: Dict[Parameter, Union[float, complex]] + ): # PauliSumOp does not allow parametrized coefficients but after binding the parameters # we need to convert it into a PauliSumOp for the PauliEvolutionGate. if isinstance(hamiltonian, SummedOp): @@ -122,7 +132,7 @@ def _try_binding_params(self, hamiltonian, hamiltonian_value_dict): self._is_op_bound(op_bound) op_list.append(op_bound) return sum(op_list) - elif isinstance(hamiltonian, PauliOp) or isinstance(hamiltonian, OperatorBase): + elif isinstance(hamiltonian, (PauliOp, OperatorBase)): if hamiltonian_value_dict is not None: op_bound = hamiltonian.bind_parameters(hamiltonian_value_dict) else: @@ -141,7 +151,7 @@ def _is_op_bound(self, op_bound): f"these parameters encountered: {op_bound.parameters}." ) - def _validate_input(self, initial_state, observable): + def _validate_input(self, initial_state, observable: OperatorBase): if initial_state is None and observable is None: raise ValueError( "TrotterQrte requires an initial state or an observable to be evolved; None " @@ -155,7 +165,7 @@ def _validate_input(self, initial_state, observable): def gradient( self, - hamiltonian: OperatorBase, + hamiltonian: Union[Pauli, PauliOp, SparsePauliOp, PauliSumOp, List], time: float, initial_state: StateFn, gradient_object: Gradient, @@ -163,7 +173,7 @@ def gradient( t_param: Parameter = None, hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, gradient_params: List[Parameter] = None, - ): + ) -> StateFn: if observable is None: raise NotImplementedError( "Observable not provided. Probability gradients are not yet supported by " @@ -217,9 +227,15 @@ def gradient( ) def _calc_time_gradient_finite_diff( - self, hamiltonian, hamiltonian_value_dict, initial_state, observable, t_param, time + self, + hamiltonian, + hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], + initial_state: StateFn, + observable: OperatorBase, + t_param: Parameter, + time: float, + epsilon: float = 0.01, ): - epsilon = 0.01 hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) evolved_state1 = self.evolve(hamiltonian, time + epsilon, initial_state, t_param=t_param) evolved_state2 = self.evolve(hamiltonian, time - epsilon, initial_state, t_param=t_param) @@ -232,11 +248,11 @@ def _calc_term_gradient( self, hamiltonian, hamiltonian_term: Union[OperatorBase, PauliSumOp, SummedOp], - initial_state, - observable, - t_param, - time, - hamiltonian_value_dict, + initial_state: StateFn, + observable: OperatorBase, + t_param: Parameter, + time: float, + hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], ): hamiltonian_term = self._try_binding_params(hamiltonian_term, hamiltonian_value_dict) custom_observable = commutator(1j * time * hamiltonian_term, observable) diff --git a/qiskit/algorithms/quantum_time_evolution/real/qrte.py b/qiskit/algorithms/quantum_time_evolution/real/qrte.py index 909ccf702f26..63cdbd12876c 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/qrte.py @@ -9,6 +9,9 @@ # 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. + +"""Interface for Quantum Real Time Evolution.""" + from abc import abstractmethod from qiskit.algorithms.quantum_time_evolution.evolution_base import EvolutionBase @@ -16,6 +19,8 @@ class Qrte(EvolutionBase): + """Base class for quantum real time evolution.""" + @abstractmethod def evolve( self, @@ -26,6 +31,10 @@ def evolve( t_param=None, hamiltonian_value_dict=None, ): + """ + Performs Quantum Real Time Evolution on an initial state according to a Hamiltonian + provided. + """ raise NotImplementedError() @abstractmethod @@ -40,4 +49,5 @@ def gradient( hamiltonian_value_dict=None, gradient_params=None, ): + """Performs Quantum Real Time Evolution of gradient expressions.""" raise NotImplementedError() From 655eb765863a2628f7713ba0b4880f58870191d5 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 14 Dec 2021 08:45:47 +0100 Subject: [PATCH 021/145] Code refactoring. --- .../trotterization/trotter_qrte.py | 4 ++-- .../trotterization/test_trotter_qrte.py | 24 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index 7b1b652991f3..cc5dab77761a 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -62,7 +62,7 @@ def __init__(self, product_formula: ProductFormula = LieTrotter()): def evolve( self, - hamiltonian: Union[Pauli, PauliOp, SparsePauliOp, PauliSumOp, List], + hamiltonian: Union[Pauli, PauliOp, SparsePauliOp, PauliSumOp], time: float, initial_state: StateFn = None, observable: OperatorBase = None, @@ -165,7 +165,7 @@ def _validate_input(self, initial_state, observable: OperatorBase): def gradient( self, - hamiltonian: Union[Pauli, PauliOp, SparsePauliOp, PauliSumOp, List], + hamiltonian: Union[Pauli, PauliOp, SparsePauliOp, PauliSumOp], time: float, initial_state: StateFn, gradient_object: Gradient, diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 900d3b463a83..653d4cdaedcd 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -19,7 +19,8 @@ from numpy.testing import assert_raises from scipy.linalg import expm -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte \ + import ( TrotterQrte, ) from qiskit.quantum_info import Statevector @@ -50,26 +51,27 @@ def test_trotter_qrte_trotter(self): evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state expected_state = ( - expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() + expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) np.testing.assert_equal(evolved_state, expected_evolved_state) + # TODO refactor with ddt def test_trotter_qrte_trotter_2(self): """Test for trotter qrte.""" operator = [(X ^ Y) + (Y ^ X), (Z ^ Z) + (Z ^ I) + (I ^ Z), Y ^ Y] # LieTrotter with 1 rep trotter_qrte = TrotterQrte() initial_state = StateFn([1, 0, 0, 0]) - evolved_state = trotter_qrte.evolve(operator, 1, initial_state) + # Calculate the expected state - expected_state = initial_state.to_matrix() for op in operator: + expected_state = initial_state.to_matrix() expected_state = expm(-1j * op.to_matrix()) @ expected_state - expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2, 2))) - - np.testing.assert_equal(evolved_state, expected_evolved_state) + expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2, 2))) + evolved_state = trotter_qrte.evolve(op, 1, initial_state) + np.testing.assert_equal(evolved_state, expected_evolved_state) def test_trotter_qrte_trotter_observable(self): """Test for trotter qrte with an observable.""" @@ -96,10 +98,10 @@ def test_trotter_qrte_suzuki(self): evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state expected_state = ( - expm(-1j * X.to_matrix() * 0.5) - @ expm(-1j * Z.to_matrix()) - @ expm(-1j * X.to_matrix() * 0.5) - @ initial_state.to_matrix() + expm(-1j * X.to_matrix() * 0.5) + @ expm(-1j * Z.to_matrix()) + @ expm(-1j * X.to_matrix() * 0.5) + @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) From 809d82983243cb89fb8e967ae3a96dab87a8c2a0 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 14 Dec 2021 11:44:42 +0100 Subject: [PATCH 022/145] Updated unit tests. --- .../trotterization/test_trotter_qrte.py | 193 +++++++++--------- 1 file changed, 99 insertions(+), 94 deletions(-) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 653d4cdaedcd..4e1243a7868e 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -13,14 +13,15 @@ """ Test Trotter Qrte. """ import unittest +import random +from ddt import ddt, data from test.python.opflow import QiskitOpflowTestCase import numpy as np from numpy.testing import assert_raises from scipy.linalg import expm -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte \ - import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( TrotterQrte, ) from qiskit.quantum_info import Statevector @@ -39,6 +40,7 @@ from qiskit.synthesis import SuzukiTrotter, QDrift +@ddt class TestTrotterQrte(QiskitOpflowTestCase): """Trotter Qrte tests.""" @@ -51,27 +53,25 @@ def test_trotter_qrte_trotter(self): evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state expected_state = ( - expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() + expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) np.testing.assert_equal(evolved_state, expected_evolved_state) - # TODO refactor with ddt - def test_trotter_qrte_trotter_2(self): + @data((X ^ Y) + (Y ^ X), (Z ^ Z) + (Z ^ I) + (I ^ Z), Y ^ Y) + def test_trotter_qrte_trotter_2(self, op): """Test for trotter qrte.""" - operator = [(X ^ Y) + (Y ^ X), (Z ^ Z) + (Z ^ I) + (I ^ Z), Y ^ Y] # LieTrotter with 1 rep trotter_qrte = TrotterQrte() initial_state = StateFn([1, 0, 0, 0]) # Calculate the expected state - for op in operator: - expected_state = initial_state.to_matrix() - expected_state = expm(-1j * op.to_matrix()) @ expected_state - expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2, 2))) - evolved_state = trotter_qrte.evolve(op, 1, initial_state) - np.testing.assert_equal(evolved_state, expected_evolved_state) + expected_state = initial_state.to_matrix() + expected_state = expm(-1j * op.to_matrix()) @ expected_state + expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2, 2))) + evolved_state = trotter_qrte.evolve(op, 1, initial_state) + np.testing.assert_equal(evolved_state, expected_evolved_state) def test_trotter_qrte_trotter_observable(self): """Test for trotter qrte with an observable.""" @@ -98,10 +98,10 @@ def test_trotter_qrte_suzuki(self): evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state expected_state = ( - expm(-1j * X.to_matrix() * 0.5) - @ expm(-1j * Z.to_matrix()) - @ expm(-1j * X.to_matrix() * 0.5) - @ initial_state.to_matrix() + expm(-1j * X.to_matrix() * 0.5) + @ expm(-1j * Z.to_matrix()) + @ expm(-1j * X.to_matrix() * 0.5) + @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) @@ -166,87 +166,92 @@ def test_trotter_qrte_gradient_summed_op_qdrift(self): expected_gradient = {theta1: 0j, theta2: 0j} np.testing.assert_equal(gradient, expected_gradient) - # # TODO fails due to Terra bug - # def test_trotter_qrte_gradient_summed_op_qdrift_2(self): - # """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" - # theta1 = Parameter("theta1") - # theta2 = Parameter("theta2") - # operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) - # mode = TrotterModeEnum.QDRIFT - # trotter_qrte = TrotterQrte(mode) - # initial_state = Zero - # time = 5 - # gradient_object = None - # observable = X ^ Y ^ Z - # value_dict = {theta1: 2, theta2: 3} - # gradient = trotter_qrte.gradient( - # operator, - # time, - # initial_state, - # gradient_object, - # observable, - # hamiltonian_value_dict=value_dict, - # gradient_params=[theta1, theta2], - # ) - # expected_gradient = {theta1: 0j, theta2: 0j} - # print(gradient) - # # np.testing.assert_equal(gradient, expected_gradient) - # - # # TODO fails due to Terra bug - # def test_trotter_qrte_gradient_summed_op_qdrift_3(self): - # """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" - # theta1 = Parameter("theta1") - # theta2 = Parameter("theta2") - # operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) - # mode = TrotterModeEnum.QDRIFT - # trotter_qrte = TrotterQrte(mode) - # initial_state = Zero - # time = 5 - # gradient_object = None - # observable = -1j * (X ^ Y ^ Z) - # value_dict = {theta1: 2, theta2: 3} - # gradient = trotter_qrte.gradient( - # operator, - # time, - # initial_state, - # gradient_object, - # observable, - # hamiltonian_value_dict=value_dict, - # gradient_params=[theta1, theta2], - # ) - # expected_gradient = {theta1: 0j, theta2: 0j} - # print(gradient) - # # np.testing.assert_equal(gradient, expected_gradient) - # - # # TODO fails due to Terra bug - # def test_trotter_qrte_gradient_summed_op_qdrift_4(self): - # """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators - # with complex parameter binding.""" - # theta1 = Parameter("theta1") - # theta2 = Parameter("theta2") - # operator = theta1 * (Z) + theta2 * (Y) - # mode = TrotterModeEnum.QDRIFT - # trotter_qrte = TrotterQrte(mode) - # initial_state = Zero - # time = 5 - # gradient_object = None - # observable = X - # value_dict = {theta1: 2, theta2: 3} - # gradient = trotter_qrte.gradient( - # operator, - # time, - # initial_state, - # gradient_object, - # observable, - # hamiltonian_value_dict=value_dict, - # gradient_params=[theta1, theta2], - # ) - # expected_gradient = {theta1: 0j, theta2: 0j} - # print(gradient) - # # np.testing.assert_equal(gradient, expected_gradient) + def test_trotter_qrte_gradient_summed_op_qdrift_2(self): + """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" + algorithm_globals.random_seed = 0 + theta1 = Parameter("theta1") + theta2 = Parameter("theta2") + operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) + trotter_qrte = TrotterQrte(QDrift()) + initial_state = Zero + time = 5 + gradient_object = None + observable = X ^ Y ^ Z + value_dict = {theta1: 2, theta2: 3} + gradient = trotter_qrte.gradient( + operator, + time, + initial_state, + gradient_object, + observable, + hamiltonian_value_dict=value_dict, + gradient_params=[theta1, theta2], + ) + expected_gradient = {theta1: 0j, theta2: 0j} + np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) + for key in expected_gradient.keys(): + np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) + + def test_trotter_qrte_gradient_summed_op_qdrift_3(self): + """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" + algorithm_globals.random_seed = 0 + theta1 = Parameter("theta1") + theta2 = Parameter("theta2") + operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) + trotter_qrte = TrotterQrte(QDrift()) + initial_state = Zero + time = 5 + gradient_object = None + observable = -1j * (X ^ Y ^ Z) + value_dict = {theta1: 2, theta2: 3} + gradient = trotter_qrte.gradient( + operator, + time, + initial_state, + gradient_object, + observable, + hamiltonian_value_dict=value_dict, + gradient_params=[theta1, theta2], + ) + expected_gradient = {theta1: 0j, theta2: 0j} + print(gradient) + np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) + for key in expected_gradient.keys(): + np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) + + # TODO fails due to Terra bug + def test_trotter_qrte_gradient_summed_op_qdrift_4(self): + """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators + with complex parameter binding.""" + algorithm_globals.random_seed = 5 + random.seed(1234) + theta1 = Parameter("theta1") + theta2 = Parameter("theta2") + operator = theta1 * (Z) + theta2 * (Y) + trotter_qrte = TrotterQrte(QDrift()) + initial_state = Zero + time = 5 + gradient_object = None + observable = X + value_dict = {theta1: 2, theta2: 3} + gradient = trotter_qrte.gradient( + operator, + time, + initial_state, + gradient_object, + observable, + hamiltonian_value_dict=value_dict, + gradient_params=[theta1, theta2], + ) + expected_gradient = {theta1: -7.1894106501296875 + 0j, theta2: 9.745099298494452 + 0j} + print(gradient) + np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) + for key in expected_gradient.keys(): + np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): """Test for trotter qrte gradient with SummedOp and QDrift; missing param.""" + algorithm_globals.random_seed = 0 theta1 = Parameter("theta1") operator = theta1 * (I ^ Z ^ I) + 5 * (Z ^ I ^ I) trotter_qrte = TrotterQrte(QDrift()) From 7e71eae818a2f23b6c489f41879b3435c85af8a5 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 14 Dec 2021 16:17:40 +0100 Subject: [PATCH 023/145] Code refactoring, extended docs and typehints. --- .../trotterization/trotter_qrte.py | 42 ++++++++++++------- .../trotterization/test_trotter_qrte.py | 18 ++++---- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index cc5dab77761a..6483fdd8f68d 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -111,15 +111,12 @@ def evolve( PauliEvolutionGate(hamiltonian[::-1], -time, synthesis=self.product_formula) ) return evolution_gate_adjoint @ observable @ evolution_gate - else: - raise ValueError( - "Did not manage to evolve because both initial_state and observable " - "provided are None." - ) def _try_binding_params( - self, hamiltonian, hamiltonian_value_dict: Dict[Parameter, Union[float, complex]] - ): + self, + hamiltonian: Union[SummedOp, PauliOp, SparsePauliOp, OperatorBase], + hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], + ) -> Union[SummedOp, PauliOp, SparsePauliOp, OperatorBase]: # PauliSumOp does not allow parametrized coefficients but after binding the parameters # we need to convert it into a PauliSumOp for the PauliEvolutionGate. if isinstance(hamiltonian, SummedOp): @@ -132,7 +129,9 @@ def _try_binding_params( self._is_op_bound(op_bound) op_list.append(op_bound) return sum(op_list) - elif isinstance(hamiltonian, (PauliOp, OperatorBase)): + elif isinstance( + hamiltonian, (PauliOp, OperatorBase, SparsePauliOp) + ): # in case there is only a single summand if hamiltonian_value_dict is not None: op_bound = hamiltonian.bind_parameters(hamiltonian_value_dict) else: @@ -141,17 +140,22 @@ def _try_binding_params( self._is_op_bound(op_bound) return op_bound else: - self._is_op_bound(hamiltonian) - return hamiltonian + raise ValueError( + f"Provided a Hamiltonian of an unsupported type: {type(hamiltonian)}. Only " + f"SummedOp, PauliOp, SparsePauliOp and OperatorBase base are supported by " + f"TrotterQrte." + ) - def _is_op_bound(self, op_bound): + def _is_op_bound(self, op_bound: Union[SummedOp, PauliOp, SparsePauliOp, OperatorBase]) -> None: + """Checks if an operator provided has all parameters bound.""" if len(op_bound.parameters) > 0: raise ValueError( f"Did not manage to bind all parameters in the Hamiltonian, " f"these parameters encountered: {op_bound.parameters}." ) - def _validate_input(self, initial_state, observable: OperatorBase): + def _validate_input(self, initial_state, observable: OperatorBase) -> None: + """Validates if one and only one among initial_state and observable is provided.""" if initial_state is None and observable is None: raise ValueError( "TrotterQrte requires an initial state or an observable to be evolved; None " @@ -165,7 +169,7 @@ def _validate_input(self, initial_state, observable: OperatorBase): def gradient( self, - hamiltonian: Union[Pauli, PauliOp, SparsePauliOp, PauliSumOp], + hamiltonian: Union[SummedOp, PauliOp, SparsePauliOp, OperatorBase], time: float, initial_state: StateFn, gradient_object: Gradient, @@ -173,7 +177,7 @@ def gradient( t_param: Parameter = None, hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, gradient_params: List[Parameter] = None, - ) -> StateFn: + ) -> Dict[Parameter, Union[float, complex]]: if observable is None: raise NotImplementedError( "Observable not provided. Probability gradients are not yet supported by " @@ -236,6 +240,8 @@ def _calc_time_gradient_finite_diff( time: float, epsilon: float = 0.01, ): + """Calculates a gradient using the finite difference method. It is used for gradients + w.r.t. a potential time parameter.""" hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) evolved_state1 = self.evolve(hamiltonian, time + epsilon, initial_state, t_param=t_param) evolved_state2 = self.evolve(hamiltonian, time - epsilon, initial_state, t_param=t_param) @@ -254,6 +260,8 @@ def _calc_term_gradient( time: float, hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], ): + """Calculates a gradient of a Hamiltonian term (a single summand) with respect to + parameters given.""" hamiltonian_term = self._try_binding_params(hamiltonian_term, hamiltonian_value_dict) custom_observable = commutator(1j * time * hamiltonian_term, observable) hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) @@ -270,7 +278,10 @@ def _calc_term_gradient( gradient = gradient.eval() return gradient + # TODO cover PauliOp, SparsePauliOp, and OperatorBase def _validate_hamiltonian_form(self, hamiltonian: SummedOp): + """Validates that a Hamiltonian is of a correct type and with expected dependence on + parameters.""" if isinstance(hamiltonian, SummedOp): if isinstance(hamiltonian.coeff, ParameterExpression): raise ValueError( @@ -287,7 +298,8 @@ def _validate_hamiltonian_form(self, hamiltonian: SummedOp): else: raise ValueError("Hamiltonian not a SummedOp which is the only option supported.") - def _is_linear_with_single_param(self, operator: OperatorBase): + def _is_linear_with_single_param(self, operator: OperatorBase) -> bool: + """Checks if an operator provided is linear w.r.t. one and only one parameter.""" if ( not isinstance(operator.coeff, ParameterExpression) and not isinstance(operator.coeff, Parameter) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 4e1243a7868e..1f8dfb72e083 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -21,7 +21,8 @@ from numpy.testing import assert_raises from scipy.linalg import expm -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte \ + import ( TrotterQrte, ) from qiskit.quantum_info import Statevector @@ -53,7 +54,7 @@ def test_trotter_qrte_trotter(self): evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state expected_state = ( - expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() + expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) @@ -98,10 +99,10 @@ def test_trotter_qrte_suzuki(self): evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state expected_state = ( - expm(-1j * X.to_matrix() * 0.5) - @ expm(-1j * Z.to_matrix()) - @ expm(-1j * X.to_matrix() * 0.5) - @ initial_state.to_matrix() + expm(-1j * X.to_matrix() * 0.5) + @ expm(-1j * Z.to_matrix()) + @ expm(-1j * X.to_matrix() * 0.5) + @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) @@ -219,7 +220,7 @@ def test_trotter_qrte_gradient_summed_op_qdrift_3(self): for key in expected_gradient.keys(): np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) - # TODO fails due to Terra bug + # TODO fix problem with randomness causing different results def test_trotter_qrte_gradient_summed_op_qdrift_4(self): """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators with complex parameter binding.""" @@ -272,6 +273,9 @@ def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): expected_gradient = {theta1: 0j} np.testing.assert_equal(gradient, expected_gradient) + # TODO unit tests for _try_binding_params, def _validate_hamiltonian_form(self, hamiltonian: + # SummedOp), _is_linear_with_single_param + if __name__ == "__main__": unittest.main() From 0cff5a83b7a54982c20358c24e5eb8330483532f Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 15 Dec 2021 14:06:16 +0100 Subject: [PATCH 024/145] Reno added. --- .../notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml diff --git a/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml b/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml new file mode 100644 index 000000000000..595e4d36f83a --- /dev/null +++ b/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Added Trotterization-based Quantum Real Time Evolution Algorithm. It is compliant with the new + Quantum Time Evolution Framework and makes use of recent + :class:`qiskit.synthesis.evolution.ProductFormula` and + :class:`qiskit.circuit.library.PauliEvolutionGate` implementations. From 5b099a29a0c9b2d745f2fd486007482e2d516e36 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 15 Dec 2021 14:11:06 +0100 Subject: [PATCH 025/145] Extended checks and unit tests. --- .../trotterization/trotter_qrte.py | 38 ++++---- .../trotterization/test_trotter_qrte.py | 87 +++++++++++-------- 2 files changed, 71 insertions(+), 54 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index 6483fdd8f68d..e215d19036c5 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -29,7 +29,7 @@ CircuitOp, ) from qiskit.circuit.library import PauliEvolutionGate -from qiskit.quantum_info import SparsePauliOp, Pauli +from qiskit.quantum_info import Pauli from qiskit.synthesis import ProductFormula, LieTrotter @@ -49,7 +49,8 @@ class TrotterQrte(Qrte): # LieTrotter with 1 rep trotter_qrte = TrotterQrte() initial_state = Zero - evolved_state = trotter_qrte.evolve(operator, 1, initial_state) + time = 1 + evolved_state = trotter_qrte.evolve(operator, time, initial_state) """ def __init__(self, product_formula: ProductFormula = LieTrotter()): @@ -62,7 +63,7 @@ def __init__(self, product_formula: ProductFormula = LieTrotter()): def evolve( self, - hamiltonian: Union[Pauli, PauliOp, SparsePauliOp, PauliSumOp], + hamiltonian: Union[Pauli, PauliOp, PauliSumOp], time: float, initial_state: StateFn = None, observable: OperatorBase = None, @@ -114,9 +115,9 @@ def evolve( def _try_binding_params( self, - hamiltonian: Union[SummedOp, PauliOp, SparsePauliOp, OperatorBase], + hamiltonian: Union[SummedOp, PauliOp, OperatorBase], hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], - ) -> Union[SummedOp, PauliOp, SparsePauliOp, OperatorBase]: + ) -> Union[SummedOp, PauliOp, OperatorBase]: # PauliSumOp does not allow parametrized coefficients but after binding the parameters # we need to convert it into a PauliSumOp for the PauliEvolutionGate. if isinstance(hamiltonian, SummedOp): @@ -130,7 +131,7 @@ def _try_binding_params( op_list.append(op_bound) return sum(op_list) elif isinstance( - hamiltonian, (PauliOp, OperatorBase, SparsePauliOp) + hamiltonian, (PauliOp, OperatorBase) ): # in case there is only a single summand if hamiltonian_value_dict is not None: op_bound = hamiltonian.bind_parameters(hamiltonian_value_dict) @@ -142,11 +143,10 @@ def _try_binding_params( else: raise ValueError( f"Provided a Hamiltonian of an unsupported type: {type(hamiltonian)}. Only " - f"SummedOp, PauliOp, SparsePauliOp and OperatorBase base are supported by " - f"TrotterQrte." + f"SummedOp, PauliOp, and OperatorBase base are supported by TrotterQrte." ) - def _is_op_bound(self, op_bound: Union[SummedOp, PauliOp, SparsePauliOp, OperatorBase]) -> None: + def _is_op_bound(self, op_bound: Union[SummedOp, PauliOp, OperatorBase]) -> None: """Checks if an operator provided has all parameters bound.""" if len(op_bound.parameters) > 0: raise ValueError( @@ -154,7 +154,7 @@ def _is_op_bound(self, op_bound: Union[SummedOp, PauliOp, SparsePauliOp, Operato f"these parameters encountered: {op_bound.parameters}." ) - def _validate_input(self, initial_state, observable: OperatorBase) -> None: + def _validate_input(self, initial_state: StateFn, observable: OperatorBase) -> None: """Validates if one and only one among initial_state and observable is provided.""" if initial_state is None and observable is None: raise ValueError( @@ -169,7 +169,7 @@ def _validate_input(self, initial_state, observable: OperatorBase) -> None: def gradient( self, - hamiltonian: Union[SummedOp, PauliOp, SparsePauliOp, OperatorBase], + hamiltonian: Union[SummedOp, PauliOp, OperatorBase], time: float, initial_state: StateFn, gradient_object: Gradient, @@ -232,7 +232,7 @@ def gradient( def _calc_time_gradient_finite_diff( self, - hamiltonian, + hamiltonian: Union[SummedOp, PauliOp, OperatorBase], hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], initial_state: StateFn, observable: OperatorBase, @@ -252,8 +252,8 @@ def _calc_time_gradient_finite_diff( def _calc_term_gradient( self, - hamiltonian, - hamiltonian_term: Union[OperatorBase, PauliSumOp, SummedOp], + hamiltonian: Union[SummedOp, PauliOp, OperatorBase], + hamiltonian_term: Union[PauliOp, OperatorBase], initial_state: StateFn, observable: OperatorBase, t_param: Parameter, @@ -278,8 +278,7 @@ def _calc_term_gradient( gradient = gradient.eval() return gradient - # TODO cover PauliOp, SparsePauliOp, and OperatorBase - def _validate_hamiltonian_form(self, hamiltonian: SummedOp): + def _validate_hamiltonian_form(self, hamiltonian: Union[SummedOp, PauliOp, OperatorBase]): """Validates that a Hamiltonian is of a correct type and with expected dependence on parameters.""" if isinstance(hamiltonian, SummedOp): @@ -294,7 +293,12 @@ def _validate_hamiltonian_form(self, hamiltonian: SummedOp): "Hamiltonian term has a coefficient that is not a linear function of a " "single parameter. It is not supported." ) - + elif isinstance(hamiltonian, (PauliOp, OperatorBase)): + if not self._is_linear_with_single_param(hamiltonian): + raise ValueError( + "Hamiltonian term has a coefficient that is not a linear function of a " + "single parameter. It is not supported." + ) else: raise ValueError("Hamiltonian not a SummedOp which is the only option supported.") diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 1f8dfb72e083..1eef3a8e0d62 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -14,18 +14,17 @@ import unittest import random -from ddt import ddt, data +from ddt import ddt, data, unpack from test.python.opflow import QiskitOpflowTestCase import numpy as np from numpy.testing import assert_raises from scipy.linalg import expm -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte \ - import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( TrotterQrte, ) -from qiskit.quantum_info import Statevector +from qiskit.quantum_info import Statevector, SparsePauliOp from qiskit.utils import algorithm_globals from qiskit.circuit import Parameter from qiskit.opflow import ( @@ -215,41 +214,40 @@ def test_trotter_qrte_gradient_summed_op_qdrift_3(self): gradient_params=[theta1, theta2], ) expected_gradient = {theta1: 0j, theta2: 0j} - print(gradient) - np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) - for key in expected_gradient.keys(): - np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) - - # TODO fix problem with randomness causing different results - def test_trotter_qrte_gradient_summed_op_qdrift_4(self): - """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators - with complex parameter binding.""" - algorithm_globals.random_seed = 5 - random.seed(1234) - theta1 = Parameter("theta1") - theta2 = Parameter("theta2") - operator = theta1 * (Z) + theta2 * (Y) - trotter_qrte = TrotterQrte(QDrift()) - initial_state = Zero - time = 5 - gradient_object = None - observable = X - value_dict = {theta1: 2, theta2: 3} - gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - hamiltonian_value_dict=value_dict, - gradient_params=[theta1, theta2], - ) - expected_gradient = {theta1: -7.1894106501296875 + 0j, theta2: 9.745099298494452 + 0j} - print(gradient) np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) for key in expected_gradient.keys(): np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) + # # TODO fix problem with randomness causing different results + # def test_trotter_qrte_gradient_summed_op_qdrift_4(self): + # """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators + # with complex parameter binding.""" + # algorithm_globals.random_seed = 5 + # random.seed(1234) + # theta1 = Parameter("theta1") + # theta2 = Parameter("theta2") + # operator = theta1 * (Z) + theta2 * (Y) + # trotter_qrte = TrotterQrte(QDrift()) + # initial_state = Zero + # time = 5 + # gradient_object = None + # observable = X + # value_dict = {theta1: 2, theta2: 3} + # gradient = trotter_qrte.gradient( + # operator, + # time, + # initial_state, + # gradient_object, + # observable, + # hamiltonian_value_dict=value_dict, + # gradient_params=[theta1, theta2], + # ) + # expected_gradient = {theta1: -7.1894106501296875 + 0j, theta2: 9.745099298494452 + 0j} + # print(gradient) + # np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) + # for key in expected_gradient.keys(): + # np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) + # def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): """Test for trotter qrte gradient with SummedOp and QDrift; missing param.""" algorithm_globals.random_seed = 0 @@ -273,8 +271,23 @@ def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): expected_gradient = {theta1: 0j} np.testing.assert_equal(gradient, expected_gradient) - # TODO unit tests for _try_binding_params, def _validate_hamiltonian_form(self, hamiltonian: - # SummedOp), _is_linear_with_single_param + @data( + (X, True), + (Parameter("theta") * Y, True), + (Parameter("theta") * Parameter("gamma") * Z, False), + (Parameter("theta") * X + Parameter("gamma") * Y, True), + (Parameter("theta1") * Parameter("theta2") * X + Parameter("gamma") * Y, False), + ) + @unpack + def test_validate_hamiltonian_form(self, hamiltonian, expected): + trotter_qrte = TrotterQrte(QDrift()) + valid = True + try: + trotter_qrte._validate_hamiltonian_form(hamiltonian) + except ValueError: + valid = False + + np.testing.assert_equal(valid, expected) if __name__ == "__main__": From 301bb6f2f6fa65d1a1a50207af8ad6008e07900b Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 15 Dec 2021 15:49:40 +0100 Subject: [PATCH 026/145] Code refactoring. --- .../quantum_time_evolution/__init__.py | 1 + .../imaginary/__init__.py | 11 --- .../imaginary/implementations/__init__.py | 11 --- .../quantum_time_evolution/imaginary/qite.py | 53 ----------- .../trotterization/trotter_ops_validator.py | 91 +++++++++++++++++++ .../trotterization/trotter_qrte.py | 83 +++-------------- .../test_trotter_ops_validator.py | 55 +++++++++++ .../trotterization/test_trotter_qrte.py | 19 ++-- 8 files changed, 169 insertions(+), 155 deletions(-) delete mode 100644 qiskit/algorithms/quantum_time_evolution/imaginary/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/imaginary/implementations/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/imaginary/qite.py create mode 100644 qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py create mode 100644 test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py diff --git a/qiskit/algorithms/quantum_time_evolution/__init__.py b/qiskit/algorithms/quantum_time_evolution/__init__.py index 96c0cf22bec9..5fef55731f89 100644 --- a/qiskit/algorithms/quantum_time_evolution/__init__.py +++ b/qiskit/algorithms/quantum_time_evolution/__init__.py @@ -9,3 +9,4 @@ # 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. +""" Quantum Time Evolution package """ diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/__init__.py b/qiskit/algorithms/quantum_time_evolution/imaginary/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/imaginary/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/__init__.py b/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/imaginary/implementations/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py b/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py deleted file mode 100644 index a9803e16a9c5..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/imaginary/qite.py +++ /dev/null @@ -1,53 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. - -"""Interface for Quantum Imaginary Time Evolution.""" - -from abc import abstractmethod - -from qiskit.algorithms.quantum_time_evolution.evolution_base import EvolutionBase -from qiskit.opflow import OperatorBase, StateFn, Gradient - - -class Qite(EvolutionBase): - """Interface for Quantum Imaginary Time Evolution.""" - - @abstractmethod - def evolve( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn = None, - observable: OperatorBase = None, - t_param=None, - hamiltonian_value_dict=None, - ): - """ - Performs Quantum Imaginary Time Evolution on an initial state according to a Hamiltonian - provided. - """ - raise NotImplementedError() - - @abstractmethod - def gradient( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn, - gradient_object: Gradient, - observable: OperatorBase = None, - t_param=None, - hamiltonian_value_dict=None, - gradient_params=None, - ): - """Performs Quantum Imaginary Time Evolution of gradient expressions.""" - raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py new file mode 100644 index 000000000000..d5c1d9a0b25a --- /dev/null +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py @@ -0,0 +1,91 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. + +"""Set of method for validating input to TrotterQrte algorithm.""" + +import numbers +from typing import Union + +from qiskit.circuit import Parameter, ParameterExpression +from qiskit.opflow import ( + OperatorBase, + StateFn, + SummedOp, + PauliOp, +) + + +def _is_op_bound(op_bound: Union[SummedOp, PauliOp, OperatorBase]) -> None: + """Checks if an operator provided has all parameters bound.""" + if len(op_bound.parameters) > 0: + raise ValueError( + f"Did not manage to bind all parameters in the Hamiltonian, " + f"these parameters encountered: {op_bound.parameters}." + ) + + +def _validate_input(initial_state: StateFn, observable: OperatorBase) -> None: + """Validates if one and only one among initial_state and observable is provided.""" + if initial_state is None and observable is None: + raise ValueError( + "TrotterQrte requires an initial state or an observable to be evolved; None " + "provided." + ) + if initial_state is not None and observable is not None: + raise ValueError( + "TrotterQrte requires an initial state or an observable to be evolved; both " + "provided." + ) + + +def _validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBase]): + """Validates that a Hamiltonian is of a correct type and with expected dependence on + parameters.""" + if isinstance(hamiltonian, SummedOp): + if isinstance(hamiltonian.coeff, ParameterExpression): + raise ValueError( + "The coefficient multiplying the whole Hamiltonian cannot be a " + "ParameterExpression." + ) + for op in hamiltonian.oplist: + if not _is_linear_with_single_param(op): + raise ValueError( + "Hamiltonian term has a coefficient that is not a linear function of a " + "single parameter. It is not supported." + ) + elif isinstance(hamiltonian, (PauliOp, OperatorBase)): + if not _is_linear_with_single_param(hamiltonian): + raise ValueError( + "Hamiltonian term has a coefficient that is not a linear function of a " + "single parameter. It is not supported." + ) + else: + raise ValueError("Hamiltonian not a SummedOp which is the only option supported.") + + +def _is_linear_with_single_param(operator: OperatorBase) -> bool: + """Checks if an operator provided is linear w.r.t. one and only one parameter.""" + if ( + not isinstance(operator.coeff, ParameterExpression) + and not isinstance(operator.coeff, Parameter) + or len(operator.coeff.parameters) == 0 + ): + return True + if len(operator.coeff.parameters) > 1: + raise ValueError( + "Term of a Hamiltonian has a coefficient that depends on several " + "parameters. Only dependence on a single parameter is allowed." + ) + single_parameter_expression = operator.coeff + parameter = list(single_parameter_expression.parameters)[0] + gradient = single_parameter_expression.gradient(parameter) + return isinstance(gradient, numbers.Number) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index e215d19036c5..569af4267b23 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -12,12 +12,16 @@ """An algorithm to implement a Trotterization real time-evolution.""" -import numbers from collections import defaultdict from typing import Union, List, Dict +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_ops_validator import ( + _validate_hamiltonian_form, + _validate_input, + _is_op_bound, +) from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte -from qiskit.circuit import Parameter, ParameterExpression +from qiskit.circuit import Parameter from qiskit.opflow import ( OperatorBase, StateFn, @@ -98,7 +102,7 @@ def evolve( ) hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) - self._validate_input(initial_state, observable) + _validate_input(initial_state, observable) # the evolution gate evolution_gate = CircuitOp( PauliEvolutionGate(hamiltonian, time, synthesis=self.product_formula) @@ -106,13 +110,15 @@ def evolve( if initial_state is not None: return (evolution_gate @ initial_state).eval() - elif observable is not None: + if observable is not None: # TODO Temporary patch due to terra bug evolution_gate_adjoint = CircuitOp( PauliEvolutionGate(hamiltonian[::-1], -time, synthesis=self.product_formula) ) return evolution_gate_adjoint @ observable @ evolution_gate + raise ValueError("Either initial_state or observable must be provided.") + def _try_binding_params( self, hamiltonian: Union[SummedOp, PauliOp, OperatorBase], @@ -127,7 +133,7 @@ def _try_binding_params( op_bound = op.bind_parameters(hamiltonian_value_dict) else: op_bound = op - self._is_op_bound(op_bound) + _is_op_bound(op_bound) op_list.append(op_bound) return sum(op_list) elif isinstance( @@ -138,7 +144,7 @@ def _try_binding_params( else: op_bound = hamiltonian - self._is_op_bound(op_bound) + _is_op_bound(op_bound) return op_bound else: raise ValueError( @@ -146,27 +152,6 @@ def _try_binding_params( f"SummedOp, PauliOp, and OperatorBase base are supported by TrotterQrte." ) - def _is_op_bound(self, op_bound: Union[SummedOp, PauliOp, OperatorBase]) -> None: - """Checks if an operator provided has all parameters bound.""" - if len(op_bound.parameters) > 0: - raise ValueError( - f"Did not manage to bind all parameters in the Hamiltonian, " - f"these parameters encountered: {op_bound.parameters}." - ) - - def _validate_input(self, initial_state: StateFn, observable: OperatorBase) -> None: - """Validates if one and only one among initial_state and observable is provided.""" - if initial_state is None and observable is None: - raise ValueError( - "TrotterQrte requires an initial state or an observable to be evolved; None " - "provided." - ) - if initial_state is not None and observable is not None: - raise ValueError( - "TrotterQrte requires an initial state or an observable to be evolved; both " - "provided." - ) - def gradient( self, hamiltonian: Union[SummedOp, PauliOp, OperatorBase], @@ -189,7 +174,7 @@ def gradient( "ignored." ) - self._validate_hamiltonian_form(hamiltonian) + _validate_hamiltonian_form(hamiltonian) param_set = set(gradient_params) if t_param is not None: param_set.add(t_param) @@ -277,45 +262,3 @@ def _calc_term_gradient( gradient = ~StateFn(custom_observable) @ evolved_state gradient = gradient.eval() return gradient - - def _validate_hamiltonian_form(self, hamiltonian: Union[SummedOp, PauliOp, OperatorBase]): - """Validates that a Hamiltonian is of a correct type and with expected dependence on - parameters.""" - if isinstance(hamiltonian, SummedOp): - if isinstance(hamiltonian.coeff, ParameterExpression): - raise ValueError( - "The coefficient multiplying the whole Hamiltonian cannot be a " - "ParameterExpression." - ) - for op in hamiltonian.oplist: - if not self._is_linear_with_single_param(op): - raise ValueError( - "Hamiltonian term has a coefficient that is not a linear function of a " - "single parameter. It is not supported." - ) - elif isinstance(hamiltonian, (PauliOp, OperatorBase)): - if not self._is_linear_with_single_param(hamiltonian): - raise ValueError( - "Hamiltonian term has a coefficient that is not a linear function of a " - "single parameter. It is not supported." - ) - else: - raise ValueError("Hamiltonian not a SummedOp which is the only option supported.") - - def _is_linear_with_single_param(self, operator: OperatorBase) -> bool: - """Checks if an operator provided is linear w.r.t. one and only one parameter.""" - if ( - not isinstance(operator.coeff, ParameterExpression) - and not isinstance(operator.coeff, Parameter) - or len(operator.coeff.parameters) == 0 - ): - return True - if len(operator.coeff.parameters) > 1: - raise ValueError( - "Term of a Hamiltonian has a coefficient that depends on several " - "parameters. Only dependence on a single parameter is allowed." - ) - single_parameter_expression = operator.coeff - parameter = list(single_parameter_expression.parameters)[0] - gradient = single_parameter_expression.gradient(parameter) - return isinstance(gradient, numbers.Number) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py new file mode 100644 index 000000000000..0a82931766c0 --- /dev/null +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py @@ -0,0 +1,55 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. + +"""Tests QRTE operators validation methods.""" + +import unittest + +from ddt import ddt, data, unpack +import numpy as np + +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_ops_validator import ( + _validate_hamiltonian_form, +) +from test.python.opflow import QiskitOpflowTestCase +from qiskit.circuit import Parameter +from qiskit.opflow import ( + X, + Z, + Y, +) + + +@ddt +class TestTrotterQrte(QiskitOpflowTestCase): + """Trotter QRTE operators validation tests.""" + + @data( + (X, True), + (Parameter("theta") * Y, True), + (Parameter("theta") * Parameter("gamma") * Z, False), + (Parameter("theta") * X + Parameter("gamma") * Y, True), + (Parameter("theta1") * Parameter("theta2") * X + Parameter("gamma") * Y, False), + ) + @unpack + def test_validate_hamiltonian_form(self, hamiltonian, expected): + valid = True + try: + _validate_hamiltonian_form(hamiltonian) + except ValueError: + valid = False + + np.testing.assert_equal(valid, expected) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 1eef3a8e0d62..fdc69b42deb5 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -13,18 +13,17 @@ """ Test Trotter Qrte. """ import unittest -import random -from ddt import ddt, data, unpack -from test.python.opflow import QiskitOpflowTestCase +from ddt import ddt, data, unpack import numpy as np from numpy.testing import assert_raises from scipy.linalg import expm +from test.python.opflow import QiskitOpflowTestCase from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( TrotterQrte, ) -from qiskit.quantum_info import Statevector, SparsePauliOp +from qiskit.quantum_info import Statevector from qiskit.utils import algorithm_globals from qiskit.circuit import Parameter from qiskit.opflow import ( @@ -53,7 +52,7 @@ def test_trotter_qrte_trotter(self): evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state expected_state = ( - expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() + expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) @@ -98,10 +97,10 @@ def test_trotter_qrte_suzuki(self): evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state expected_state = ( - expm(-1j * X.to_matrix() * 0.5) - @ expm(-1j * Z.to_matrix()) - @ expm(-1j * X.to_matrix() * 0.5) - @ initial_state.to_matrix() + expm(-1j * X.to_matrix() * 0.5) + @ expm(-1j * Z.to_matrix()) + @ expm(-1j * X.to_matrix() * 0.5) + @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) @@ -283,7 +282,7 @@ def test_validate_hamiltonian_form(self, hamiltonian, expected): trotter_qrte = TrotterQrte(QDrift()) valid = True try: - trotter_qrte._validate_hamiltonian_form(hamiltonian) + _validate_hamiltonian_form(hamiltonian) except ValueError: valid = False From b6e7689fcef6e029584e0f6aa2e2116597fd6214 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 15 Dec 2021 15:54:34 +0100 Subject: [PATCH 027/145] Code refactoring. --- .../trotterization/trotter_qrte.py | 1 + .../trotterization/test_trotter_qrte.py | 20 +------------------ 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index 569af4267b23..0e5c79620558 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -181,6 +181,7 @@ def gradient( if param_set.issubset(set(hamiltonian.parameters)): gradients = defaultdict(float) + # TODO support PauliOp, OperatorBase if isinstance(hamiltonian, SummedOp): for gradient_param in param_set: for hamiltonian_term in hamiltonian.oplist: diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index fdc69b42deb5..8c47e4f4e1e2 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -14,7 +14,7 @@ import unittest -from ddt import ddt, data, unpack +from ddt import ddt, data import numpy as np from numpy.testing import assert_raises from scipy.linalg import expm @@ -270,24 +270,6 @@ def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): expected_gradient = {theta1: 0j} np.testing.assert_equal(gradient, expected_gradient) - @data( - (X, True), - (Parameter("theta") * Y, True), - (Parameter("theta") * Parameter("gamma") * Z, False), - (Parameter("theta") * X + Parameter("gamma") * Y, True), - (Parameter("theta1") * Parameter("theta2") * X + Parameter("gamma") * Y, False), - ) - @unpack - def test_validate_hamiltonian_form(self, hamiltonian, expected): - trotter_qrte = TrotterQrte(QDrift()) - valid = True - try: - _validate_hamiltonian_form(hamiltonian) - except ValueError: - valid = False - - np.testing.assert_equal(valid, expected) - if __name__ == "__main__": unittest.main() From 4a6b8b0fd195af52fa4fb22fcde549a9819bc8ad Mon Sep 17 00:00:00 2001 From: "acv@zurich.ibm.com" Date: Wed, 15 Dec 2021 16:54:34 +0100 Subject: [PATCH 028/145] fix problem with test --- .../real/implementations/trotterization/trotter_qrte.py | 4 +++- .../implementations/trotterization/test_trotter_qrte.py | 7 ++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index 6483fdd8f68d..8dffb6c24f54 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -197,7 +197,9 @@ def gradient( if param_set.issubset(set(hamiltonian.parameters)): gradients = defaultdict(float) if isinstance(hamiltonian, SummedOp): - for gradient_param in param_set: + # access through gradient_params rather param_set for testing purposes. set() adds + # the elements in random order and otherwise there is no way to seed the results. + for gradient_param in gradient_params: for hamiltonian_term in hamiltonian.oplist: if gradient_param in hamiltonian_term.parameters: if gradient_param == t_param: diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 1f8dfb72e083..890c43ce091b 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -215,7 +215,6 @@ def test_trotter_qrte_gradient_summed_op_qdrift_3(self): gradient_params=[theta1, theta2], ) expected_gradient = {theta1: 0j, theta2: 0j} - print(gradient) np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) for key in expected_gradient.keys(): np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) @@ -224,8 +223,7 @@ def test_trotter_qrte_gradient_summed_op_qdrift_3(self): def test_trotter_qrte_gradient_summed_op_qdrift_4(self): """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators with complex parameter binding.""" - algorithm_globals.random_seed = 5 - random.seed(1234) + algorithm_globals.random_seed = 0 theta1 = Parameter("theta1") theta2 = Parameter("theta2") operator = theta1 * (Z) + theta2 * (Y) @@ -244,8 +242,7 @@ def test_trotter_qrte_gradient_summed_op_qdrift_4(self): hamiltonian_value_dict=value_dict, gradient_params=[theta1, theta2], ) - expected_gradient = {theta1: -7.1894106501296875 + 0j, theta2: 9.745099298494452 + 0j} - print(gradient) + expected_gradient = {theta1: 10.938002663771012 + 0j, theta2: 9.745099298494452 + 0j} np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) for key in expected_gradient.keys(): np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) From d690b440e9f8b06e0327ea12eda44233baabd65d Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 17 Dec 2021 14:09:13 +0100 Subject: [PATCH 029/145] Docs extended. --- .../quantum_time_evolution/real/__init__.py | 1 + .../trotterization/__init__.py | 14 +++ .../trotterization/trotter_ops_validator.py | 30 ++++-- .../trotterization/trotter_qrte.py | 93 ++++++++++++++++++- .../trotterization/test_trotter_qrte.py | 13 ++- 5 files changed, 134 insertions(+), 17 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/__init__.py b/qiskit/algorithms/quantum_time_evolution/real/__init__.py index 96c0cf22bec9..156d4bf6c9a7 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/__init__.py +++ b/qiskit/algorithms/quantum_time_evolution/real/__init__.py @@ -9,3 +9,4 @@ # 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. +""" Quantum Real Time Evolution package """ diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py index 96c0cf22bec9..fc6912b00177 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py @@ -9,3 +9,17 @@ # 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. +"""This package contains Trotterization-based Quantum Real Time Evolution algorithm. +It is compliant with the new Quantum Time Evolution Framework and makes use ProductFormula and +PauliEvolutionGate implementations. +The evolution with gradients assumes that a Hamiltonian is a linear combination of PauliOp objects +w.r.t. given parameters. It case of a single summand, it might be a PauliOp, or an OperatorBase. +Gradients are taken w.r.t. to a time parameter using the finite difference method (if +a Hamiltonian is time-dependent via t_param = Parameter("t")) and/or w.r.t. parameters present in a +Hamiltonian using an expectation value through a custom observable.""" + +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( + TrotterQrte, +) + +__all__ = ["TrotterQrte"] diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py index d5c1d9a0b25a..faed35320213 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py @@ -24,12 +24,17 @@ ) -def _is_op_bound(op_bound: Union[SummedOp, PauliOp, OperatorBase]) -> None: - """Checks if an operator provided has all parameters bound.""" - if len(op_bound.parameters) > 0: +def _is_op_bound(operator: Union[SummedOp, PauliOp, OperatorBase]) -> None: + """Checks if an operator provided has all parameters bound. + Args: + operator: Operator to be checked. + Raises: + ValueError: If an operator has unbound parameters. + """ + if len(operator.parameters) > 0: raise ValueError( f"Did not manage to bind all parameters in the Hamiltonian, " - f"these parameters encountered: {op_bound.parameters}." + f"these parameters encountered: {operator.parameters}." ) @@ -49,7 +54,12 @@ def _validate_input(initial_state: StateFn, observable: OperatorBase) -> None: def _validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBase]): """Validates that a Hamiltonian is of a correct type and with expected dependence on - parameters.""" + parameters. + Args: + hamiltonian: Hamiltonian to be validated. + Raises: + ValueError: if an invalid Hamiltonian is provided. + """ if isinstance(hamiltonian, SummedOp): if isinstance(hamiltonian.coeff, ParameterExpression): raise ValueError( @@ -73,7 +83,15 @@ def _validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBas def _is_linear_with_single_param(operator: OperatorBase) -> bool: - """Checks if an operator provided is linear w.r.t. one and only one parameter.""" + """Checks if an operator provided is linear w.r.t. one and only one parameter. + Args: + operator: Operator to be checked. + Returns: + True or False depending on whether an operator is linear in a single param and only contains' + a single param. + Raises: + ValueError: If an operator contains more than 1 parameter. + """ if ( not isinstance(operator.coeff, ParameterExpression) and not isinstance(operator.coeff, Parameter) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index bc0c47a3f5f1..ee3d136c26b0 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -75,6 +75,9 @@ def evolve( hamiltonian_value_dict: Dict[Parameter, Union[float, complex]] = None, ) -> StateFn: """ + Evolves a quantum state or an observable for a given time using the Trotterization method + based on a product formula provided. + Time-dependent Hamiltonians are not yet supported. Args: hamiltonian: The operator to evolve. Can also be provided as list of non-commuting @@ -124,6 +127,22 @@ def _try_binding_params( hamiltonian: Union[SummedOp, PauliOp, OperatorBase], hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], ) -> Union[SummedOp, PauliOp, OperatorBase]: + """ + Tries binding parameters in a Hamiltonian. + Args: + hamiltonian: + The operator to evolve. Can also be provided as list of non-commuting + operators where the elements are sums of commuting operators. + For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. + hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to + certain values. + + Returns: + Bound Hamiltonian. + + Raises: + ValueError: If a Hamiltonian is not of an expected type. + """ # PauliSumOp does not allow parametrized coefficients but after binding the parameters # we need to convert it into a PauliSumOp for the PauliEvolutionGate. if isinstance(hamiltonian, SummedOp): @@ -163,6 +182,34 @@ def gradient( hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, gradient_params: List[Parameter] = None, ) -> Dict[Parameter, Union[float, complex]]: + """ + Computes gradients of a quantum state evolved according to a given Hamiltonian and w.r.t. + observable and parameters provided. + Args: + hamiltonian: The operator to evolve. Can also be provided as list of non-commuting + operators where the elements are sums of commuting operators. + For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. + time: Total time of evolution. + initial_state: If interested in a quantum state time evolution, a quantum state to be + evolved. + gradient_object: TrotterQrte does not support custom Gradient method. Provided Gradient + object is ignored. + observable: If interested in a quantum observable time evolution, a quantum observable + to be evolved. + t_param: Not supported by this algorithm. + hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to + certain values. + gradient_params: Parameters w.r.t. which gradients shall be computed. + + Returns: + The dictionary of parameters and respective evolved gradients. + + Raises: + ValueError: If a Hamiltonian is not of an expected form and/or type or a gradient w.r.t. + time is requested. + NotImplementedError: If no observable is provided. + Warning: If a gradient_object is provided. + """ if observable is None: raise NotImplementedError( "Observable not provided. Probability gradients are not yet supported by " @@ -173,6 +220,11 @@ def gradient( "TrotterQrte does not support custom Gradient method. Provided Gradient object is " "ignored." ) + if t_param is not None: + raise ValueError( + "TrotterQrte does not accept a time dependent hamiltonian," + "t_param should be set to None." + ) _validate_hamiltonian_form(hamiltonian) param_set = set(gradient_params) @@ -227,9 +279,26 @@ def _calc_time_gradient_finite_diff( t_param: Parameter, time: float, epsilon: float = 0.01, - ): + ) -> float: """Calculates a gradient using the finite difference method. It is used for gradients - w.r.t. a potential time parameter.""" + w.r.t. a potential time parameter. + Args: + hamiltonian: The operator to evolve. Can also be provided as list of non-commuting + operators where the elements are sums of commuting operators. + For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. + hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to + certain values. + initial_state: If interested in a quantum state time evolution, a quantum state to be + evolved. + observable: If interested in a quantum observable time evolution, a quantum observable + to be evolved. + t_param: Not supported by this algorithm. + time: Total time of evolution. + epsilon: Size of a finite difference interval. + + Returns: + Finite difference gradient w.r.t. time parameter of an evolved state. + """ hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) evolved_state1 = self.evolve(hamiltonian, time + epsilon, initial_state, t_param=t_param) evolved_state2 = self.evolve(hamiltonian, time - epsilon, initial_state, t_param=t_param) @@ -247,9 +316,25 @@ def _calc_term_gradient( t_param: Parameter, time: float, hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], - ): + ) -> float: """Calculates a gradient of a Hamiltonian term (a single summand) with respect to - parameters given.""" + parameters given. + Args: + hamiltonian: The operator to evolve. Can also be provided as list of non-commuting + operators where the elements are sums of commuting operators. + For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. + hamiltonian_term: One of the terms (summands) from a Hamiltonian. + initial_state: If interested in a quantum state time evolution, a quantum state to be + evolved. + observable: If interested in a quantum observable time evolution, a quantum observable + to be evolved. + t_param: Not supported by this algorithm. + time: Total time of evolution. + hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to + certain values. + Returns: + Gradient of an evolved state w.r.t. parameters from a hamiltonian_value_dict. + """ hamiltonian_term = self._try_binding_params(hamiltonian_term, hamiltonian_value_dict) custom_observable = commutator(1j * time * hamiltonian_term, observable) hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 890c43ce091b..1967eebc8149 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -21,8 +21,7 @@ from numpy.testing import assert_raises from scipy.linalg import expm -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte \ - import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( TrotterQrte, ) from qiskit.quantum_info import Statevector @@ -54,7 +53,7 @@ def test_trotter_qrte_trotter(self): evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state expected_state = ( - expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() + expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) @@ -99,10 +98,10 @@ def test_trotter_qrte_suzuki(self): evolved_state = trotter_qrte.evolve(operator, 1, initial_state) # Calculate the expected state expected_state = ( - expm(-1j * X.to_matrix() * 0.5) - @ expm(-1j * Z.to_matrix()) - @ expm(-1j * X.to_matrix() * 0.5) - @ initial_state.to_matrix() + expm(-1j * X.to_matrix() * 0.5) + @ expm(-1j * Z.to_matrix()) + @ expm(-1j * X.to_matrix() * 0.5) + @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) From a2c64a4bb81e8812ade28596195c0361b19b0f04 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 17 Dec 2021 16:17:24 +0100 Subject: [PATCH 030/145] Added support for PauliOp, code refactoring. --- .../trotterization/trotter_qrte.py | 90 ++++++++++++------- .../trotterization/test_trotter_qrte.py | 5 +- 2 files changed, 62 insertions(+), 33 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index ee3d136c26b0..961e7cbbd16e 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -13,7 +13,8 @@ """An algorithm to implement a Trotterization real time-evolution.""" from collections import defaultdict -from typing import Union, List, Dict +from itertools import product +from typing import Union, List, Dict, Optional from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_ops_validator import ( _validate_hamiltonian_form, @@ -122,8 +123,8 @@ def evolve( raise ValueError("Either initial_state or observable must be provided.") + @staticmethod def _try_binding_params( - self, hamiltonian: Union[SummedOp, PauliOp, OperatorBase], hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], ) -> Union[SummedOp, PauliOp, OperatorBase]: @@ -228,39 +229,34 @@ def gradient( _validate_hamiltonian_form(hamiltonian) param_set = set(gradient_params) - if t_param is not None: - param_set.add(t_param) + + # when t_param becomes supported + # if t_param is not None: + # param_set.add(t_param) + + if isinstance(hamiltonian, PauliOp): + hamiltonian = SummedOp([hamiltonian]) if param_set.issubset(set(hamiltonian.parameters)): gradients = defaultdict(float) - # TODO support PauliOp, OperatorBase if isinstance(hamiltonian, SummedOp): # access through gradient_params rather param_set for testing purposes. set() adds # the elements in random order and otherwise there is no way to seed the results. - for gradient_param in gradient_params: - for hamiltonian_term in hamiltonian.oplist: - if gradient_param in hamiltonian_term.parameters: - if gradient_param == t_param: - finite_difference = self._calc_time_gradient_finite_diff( - hamiltonian, - hamiltonian_value_dict, - initial_state, - observable, - t_param, - time, - ) - gradients[gradient_param] += finite_difference.eval() - else: - gradient = self._calc_term_gradient( - hamiltonian, - hamiltonian_term, - initial_state, - observable, - t_param, - time, - hamiltonian_value_dict, - ) - gradients[gradient_param] += gradient + for gradient_param, hamiltonian_term in product( + gradient_params, hamiltonian.oplist + ): + if gradient_param in hamiltonian_term.parameters: + self._calc_term_gradient_by_type( + gradient_param, + gradients, + hamiltonian, + hamiltonian_term, + hamiltonian_value_dict, + initial_state, + observable, + t_param, + time, + ) return gradients @@ -270,6 +266,40 @@ def gradient( "and not a t_param." ) + def _calc_term_gradient_by_type( + self, + gradient_param, + gradients, + hamiltonian, + hamiltonian_term, + hamiltonian_value_dict, + initial_state, + observable, + t_param, + time, + ): + if gradient_param == t_param: + finite_difference = self._calc_time_gradient_finite_diff( + hamiltonian, + hamiltonian_value_dict, + initial_state, + observable, + t_param, + time, + ) + gradients[gradient_param] += finite_difference.eval() + else: + gradient = self._calc_term_gradient( + hamiltonian, + hamiltonian_term, + initial_state, + observable, + t_param, + time, + hamiltonian_value_dict, + ) + gradients[gradient_param] += gradient + def _calc_time_gradient_finite_diff( self, hamiltonian: Union[SummedOp, PauliOp, OperatorBase], @@ -313,7 +343,7 @@ def _calc_term_gradient( hamiltonian_term: Union[PauliOp, OperatorBase], initial_state: StateFn, observable: OperatorBase, - t_param: Parameter, + t_param: Optional[Parameter], time: float, hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], ) -> float: diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 1967eebc8149..c73e8fbbcd95 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -13,14 +13,13 @@ """ Test Trotter Qrte. """ import unittest -import random -from ddt import ddt, data -from test.python.opflow import QiskitOpflowTestCase +from ddt import ddt, data import numpy as np from numpy.testing import assert_raises from scipy.linalg import expm +from test.python.opflow import QiskitOpflowTestCase from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( TrotterQrte, ) From 2701ef7a141f27ccfcf61db8ba1762797bed535e Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 16 Feb 2022 11:13:09 +0100 Subject: [PATCH 031/145] Implemented general Quantum Time Evolution Framework interfaces. --- qiskit/algorithms/time_evolution/__init__.py | 12 ++++ .../time_evolution/evolution_base.py | 63 +++++++++++++++++++ .../time_evolution/evolution_result.py | 39 ++++++++++++ .../time_evolution/imaginary/__init__.py | 11 ++++ .../time_evolution/imaginary/qite.py | 21 +++++++ .../time_evolution/real/__init__.py | 11 ++++ qiskit/algorithms/time_evolution/real/qrte.py | 21 +++++++ 7 files changed, 178 insertions(+) create mode 100644 qiskit/algorithms/time_evolution/__init__.py create mode 100644 qiskit/algorithms/time_evolution/evolution_base.py create mode 100644 qiskit/algorithms/time_evolution/evolution_result.py create mode 100644 qiskit/algorithms/time_evolution/imaginary/__init__.py create mode 100644 qiskit/algorithms/time_evolution/imaginary/qite.py create mode 100644 qiskit/algorithms/time_evolution/real/__init__.py create mode 100644 qiskit/algorithms/time_evolution/real/qrte.py diff --git a/qiskit/algorithms/time_evolution/__init__.py b/qiskit/algorithms/time_evolution/__init__.py new file mode 100644 index 000000000000..805bbc1b8eba --- /dev/null +++ b/qiskit/algorithms/time_evolution/__init__.py @@ -0,0 +1,12 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021, 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. +""" Quantum Time Evolution package """ diff --git a/qiskit/algorithms/time_evolution/evolution_base.py b/qiskit/algorithms/time_evolution/evolution_base.py new file mode 100644 index 000000000000..c40b20ae9c4d --- /dev/null +++ b/qiskit/algorithms/time_evolution/evolution_base.py @@ -0,0 +1,63 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021, 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. + +"""Base class for quantum time evolution.""" + +from abc import ABC, abstractmethod +from typing import Union, List, Optional, Dict + +from qiskit.circuit import Parameter +from qiskit.opflow import OperatorBase, StateFn, Gradient + + +class EvolutionBase(ABC): + """Base class for quantum time evolution.""" + + @abstractmethod + def evolve( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: Optional[StateFn] = None, + observable: Optional[OperatorBase] = None, + t_param: Optional[Parameter] = None, + hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, + ): + """ + Evolves an initial state or an observable according to a Hamiltonian provided. + Args: + hamiltonian: + ⟨ψ(ω)|H|ψ(ω)〉 + Operator used variational time evolution. + time: Total time of evolution. + initial_state: Quantum state to be evolved. + observable: Observable to be evolved. + t_param: Time parameter in case of a time-dependent Hamiltonian. + hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to + certain values, including the t_param. + """ + raise NotImplementedError() + + @abstractmethod + def gradient( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn, + gradient_object: Gradient, + observable: Optional[OperatorBase] = None, + t_param: Optional[Parameter] = None, + hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, + gradient_params: Optional[List[Parameter]] = None, + ): + """Performs Quantum Time Evolution of gradient expressions.""" + raise NotImplementedError() diff --git a/qiskit/algorithms/time_evolution/evolution_result.py b/qiskit/algorithms/time_evolution/evolution_result.py new file mode 100644 index 000000000000..bdd633c8f372 --- /dev/null +++ b/qiskit/algorithms/time_evolution/evolution_result.py @@ -0,0 +1,39 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021, 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. +"""Class for holding evolution result and relevant metadata.""" +from typing import Dict, Any, Optional + +from qiskit.opflow import OperatorBase + + +class EvolutionResult: + """Class for holding evolution result and relevant metadata.""" + + def __init__(self, evolved_object: OperatorBase, metadata: Optional[Dict[str, Any]] = None): + """ + Args: + evolved_object: An evolved quantum state or an evolved quantum observable. + metadata: A dictionary with algorithm-specific metadata. Keys contain strings that name + data stores as a corresponding value. + """ + self._evolved_object = evolved_object + self._metadata = metadata + + @property + def evolved_object(self): + """Returns an evolved quantum state or an evolved quantum observable.""" + return self._evolved_object + + @property + def metadata(self): + """Returns a dictionary with algorithm-specific metadata.""" + return self._metadata diff --git a/qiskit/algorithms/time_evolution/imaginary/__init__.py b/qiskit/algorithms/time_evolution/imaginary/__init__.py new file mode 100644 index 000000000000..b3ac36d2a6d9 --- /dev/null +++ b/qiskit/algorithms/time_evolution/imaginary/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021, 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. diff --git a/qiskit/algorithms/time_evolution/imaginary/qite.py b/qiskit/algorithms/time_evolution/imaginary/qite.py new file mode 100644 index 000000000000..ab5fafd0fbba --- /dev/null +++ b/qiskit/algorithms/time_evolution/imaginary/qite.py @@ -0,0 +1,21 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021, 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. + +"""Interface for Quantum Imaginary Time Evolution.""" + +from abc import ABC + +from qiskit.algorithms.time_evolution.evolution_base import EvolutionBase + + +class Qite(EvolutionBase, ABC): + """Interface for Quantum Imaginary Time Evolution.""" diff --git a/qiskit/algorithms/time_evolution/real/__init__.py b/qiskit/algorithms/time_evolution/real/__init__.py new file mode 100644 index 000000000000..b3ac36d2a6d9 --- /dev/null +++ b/qiskit/algorithms/time_evolution/real/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021, 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. diff --git a/qiskit/algorithms/time_evolution/real/qrte.py b/qiskit/algorithms/time_evolution/real/qrte.py new file mode 100644 index 000000000000..1e352b32730a --- /dev/null +++ b/qiskit/algorithms/time_evolution/real/qrte.py @@ -0,0 +1,21 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021, 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. + +"""Interface for Quantum Real Time Evolution.""" + +from abc import ABC + +from qiskit.algorithms.time_evolution.evolution_base import EvolutionBase + + +class Qrte(EvolutionBase, ABC): + """Base class for quantum real time evolution.""" From d67b0c961b76871828687286be544040c28f24f2 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 16 Feb 2022 12:11:21 +0100 Subject: [PATCH 032/145] Updated docs. --- .../time_evolution/evolution_base.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/qiskit/algorithms/time_evolution/evolution_base.py b/qiskit/algorithms/time_evolution/evolution_base.py index c40b20ae9c4d..e7d95578f17e 100644 --- a/qiskit/algorithms/time_evolution/evolution_base.py +++ b/qiskit/algorithms/time_evolution/evolution_base.py @@ -35,9 +35,7 @@ def evolve( """ Evolves an initial state or an observable according to a Hamiltonian provided. Args: - hamiltonian: - ⟨ψ(ω)|H|ψ(ω)〉 - Operator used variational time evolution. + hamiltonian: Operator used for variational time evolution. time: Total time of evolution. initial_state: Quantum state to be evolved. observable: Observable to be evolved. @@ -59,5 +57,18 @@ def gradient( hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, gradient_params: Optional[List[Parameter]] = None, ): - """Performs Quantum Time Evolution of gradient expressions.""" + """Performs Quantum Time Evolution of gradient expressions. + Args: + hamiltonian: Operator used for variational time evolution. + time: Total time of evolution. + initial_state: Quantum state to be evolved. + gradient_object: ``Gradient`` object which defines a method for computing desired + gradients. + observable: Observable to be evolved. + t_param: Time parameter in case of a time-dependent Hamiltonian. + hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to + certain values, including the t_param. + gradient_params: List of ``Parameter`` instances that indicate gradients with respect to + which parameters shall be computed. + """ raise NotImplementedError() From 5e1065ec5a93950e6375f853c655a074f4a21761 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 16 Feb 2022 12:18:01 +0100 Subject: [PATCH 033/145] Reno added. --- .../notes/time-evo-framework-9d58827bdbbebd62.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml diff --git a/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml b/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml new file mode 100644 index 000000000000..cb1da760b9fe --- /dev/null +++ b/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + This change introduces the unified framework for Quantum Time Evolution for imaginary and real + case. This contribution includes relevant interfaces that will be implement by many algorithms. + It covers any time evolution algorithm for evolving quantum states or observables, including + time-dependent Hamiltonians. From 9c041ed413d5eaf9de4f97581acbc2f2ceebe4da Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 16 Feb 2022 13:48:22 +0100 Subject: [PATCH 034/145] Improved reno. --- .../notes/time-evo-framework-9d58827bdbbebd62.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml b/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml index cb1da760b9fe..5c7bbf513b3e 100644 --- a/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml +++ b/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml @@ -1,7 +1,11 @@ --- features: - | - This change introduces the unified framework for Quantum Time Evolution for imaginary and real - case. This contribution includes relevant interfaces that will be implement by many algorithms. - It covers any time evolution algorithm for evolving quantum states or observables, including - time-dependent Hamiltonians. + Interfaces for the unified framework for Quantum Time Evolution are introduced. + :py:class:`~qiskit.algorithms.time_evolution.evolution_base` interface serves as a base for + any time evolution algorithm for evolving quantum states or observables, including + time-dependent Hamiltonians. :py:class:`~qiskit.algorithms.time_evolution.imaginary.qite` and + :py:class:`~qiskit.algorithms.time_evolution.imaginary.qrte` are derived interfaces for + imaginary and real time evolution cases respectively. + :py:class:`~qiskit.algorithms.time_evolution.evolution_result` is introduced as a result object + for quantum time evolution algorithms. From 5582d32854e77f6c3af6ac498323d1d6f2dc9d31 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 16 Feb 2022 13:58:02 +0100 Subject: [PATCH 035/145] Code refactoring. --- .../time_evolution/evolution_base.py | 32 +++++++++++++------ .../time_evolution/evolution_result.py | 8 ++--- .../time_evolution/imaginary/qite.py | 2 +- qiskit/algorithms/time_evolution/real/qrte.py | 2 +- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/qiskit/algorithms/time_evolution/evolution_base.py b/qiskit/algorithms/time_evolution/evolution_base.py index e7d95578f17e..a0a2e06398dc 100644 --- a/qiskit/algorithms/time_evolution/evolution_base.py +++ b/qiskit/algorithms/time_evolution/evolution_base.py @@ -15,6 +15,7 @@ from abc import ABC, abstractmethod from typing import Union, List, Optional, Dict +from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult from qiskit.circuit import Parameter from qiskit.opflow import OperatorBase, StateFn, Gradient @@ -31,17 +32,22 @@ def evolve( observable: Optional[OperatorBase] = None, t_param: Optional[Parameter] = None, hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, - ): + ) -> EvolutionResult: """ Evolves an initial state or an observable according to a Hamiltonian provided. + Args: hamiltonian: Operator used for variational time evolution. time: Total time of evolution. initial_state: Quantum state to be evolved. observable: Observable to be evolved. t_param: Time parameter in case of a time-dependent Hamiltonian. - hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values, including the t_param. + hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to certain + values, including the t_param. + + Returns: + Evolution result which includes an evolved gradient of quantum state or an observable + and metadata. """ raise NotImplementedError() @@ -56,19 +62,25 @@ def gradient( t_param: Optional[Parameter] = None, hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, gradient_params: Optional[List[Parameter]] = None, - ): - """Performs Quantum Time Evolution of gradient expressions. + ) -> EvolutionResult: + """ + Performs Quantum Time Evolution of gradient expressions. + Args: hamiltonian: Operator used for variational time evolution. time: Total time of evolution. initial_state: Quantum state to be evolved. - gradient_object: ``Gradient`` object which defines a method for computing desired - gradients. + gradient_object: Gradient object which defines a method for computing desired + gradients. observable: Observable to be evolved. t_param: Time parameter in case of a time-dependent Hamiltonian. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values, including the t_param. - gradient_params: List of ``Parameter`` instances that indicate gradients with respect to - which parameters shall be computed. + certain values, including the t_param. + gradient_params: List of parameters that indicates with respect to which parameters + gradients shall be computed. + + Returns: + Evolution result which includes an evolved gradient of quantum state or an observable + and metadata. """ raise NotImplementedError() diff --git a/qiskit/algorithms/time_evolution/evolution_result.py b/qiskit/algorithms/time_evolution/evolution_result.py index bdd633c8f372..72040728130c 100644 --- a/qiskit/algorithms/time_evolution/evolution_result.py +++ b/qiskit/algorithms/time_evolution/evolution_result.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """Class for holding evolution result and relevant metadata.""" -from typing import Dict, Any, Optional +from typing import Dict, Any, Optional, Union from qiskit.opflow import OperatorBase @@ -23,17 +23,17 @@ def __init__(self, evolved_object: OperatorBase, metadata: Optional[Dict[str, An Args: evolved_object: An evolved quantum state or an evolved quantum observable. metadata: A dictionary with algorithm-specific metadata. Keys contain strings that name - data stores as a corresponding value. + data stores as a corresponding value. """ self._evolved_object = evolved_object self._metadata = metadata @property - def evolved_object(self): + def evolved_object(self) -> OperatorBase: """Returns an evolved quantum state or an evolved quantum observable.""" return self._evolved_object @property - def metadata(self): + def metadata(self) -> Union[Dict[str, Any], None]: """Returns a dictionary with algorithm-specific metadata.""" return self._metadata diff --git a/qiskit/algorithms/time_evolution/imaginary/qite.py b/qiskit/algorithms/time_evolution/imaginary/qite.py index ab5fafd0fbba..7dd8006607c0 100644 --- a/qiskit/algorithms/time_evolution/imaginary/qite.py +++ b/qiskit/algorithms/time_evolution/imaginary/qite.py @@ -17,5 +17,5 @@ from qiskit.algorithms.time_evolution.evolution_base import EvolutionBase -class Qite(EvolutionBase, ABC): +class QITE(EvolutionBase, ABC): """Interface for Quantum Imaginary Time Evolution.""" diff --git a/qiskit/algorithms/time_evolution/real/qrte.py b/qiskit/algorithms/time_evolution/real/qrte.py index 1e352b32730a..54f85c9b955b 100644 --- a/qiskit/algorithms/time_evolution/real/qrte.py +++ b/qiskit/algorithms/time_evolution/real/qrte.py @@ -17,5 +17,5 @@ from qiskit.algorithms.time_evolution.evolution_base import EvolutionBase -class Qrte(EvolutionBase, ABC): +class QRTE(EvolutionBase, ABC): """Base class for quantum real time evolution.""" From c0e4bdd4d18a68a43b65358658e2728a69c963d4 Mon Sep 17 00:00:00 2001 From: dlasecki Date: Tue, 22 Feb 2022 14:54:21 +0100 Subject: [PATCH 036/145] Update qiskit/algorithms/time_evolution/evolution_base.py Co-authored-by: Julien Gacon --- qiskit/algorithms/time_evolution/evolution_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/algorithms/time_evolution/evolution_base.py b/qiskit/algorithms/time_evolution/evolution_base.py index a0a2e06398dc..6dda1ec7135f 100644 --- a/qiskit/algorithms/time_evolution/evolution_base.py +++ b/qiskit/algorithms/time_evolution/evolution_base.py @@ -37,7 +37,7 @@ def evolve( Evolves an initial state or an observable according to a Hamiltonian provided. Args: - hamiltonian: Operator used for variational time evolution. + hamiltonian: The Hamiltonian under which to evolve the system. time: Total time of evolution. initial_state: Quantum state to be evolved. observable: Observable to be evolved. From d9dbcba78f7b76f8842373e106be36d2c5081e28 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 22 Feb 2022 16:29:25 +0100 Subject: [PATCH 037/145] Code refactoring. --- .../time_evolution/evolution_base.py | 4 +-- .../time_evolution/evolution_result.py | 28 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/qiskit/algorithms/time_evolution/evolution_base.py b/qiskit/algorithms/time_evolution/evolution_base.py index a0a2e06398dc..b796161f6625 100644 --- a/qiskit/algorithms/time_evolution/evolution_base.py +++ b/qiskit/algorithms/time_evolution/evolution_base.py @@ -39,8 +39,8 @@ def evolve( Args: hamiltonian: Operator used for variational time evolution. time: Total time of evolution. - initial_state: Quantum state to be evolved. - observable: Observable to be evolved. + initial_state: Quantum state to be evolved; mutually exclusive with observable. + observable: Observable to be evolved; mutually exclusive with initial_state. t_param: Time parameter in case of a time-dependent Hamiltonian. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to certain values, including the t_param. diff --git a/qiskit/algorithms/time_evolution/evolution_result.py b/qiskit/algorithms/time_evolution/evolution_result.py index 72040728130c..459aae519fa9 100644 --- a/qiskit/algorithms/time_evolution/evolution_result.py +++ b/qiskit/algorithms/time_evolution/evolution_result.py @@ -10,30 +10,30 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """Class for holding evolution result and relevant metadata.""" -from typing import Dict, Any, Optional, Union +from qiskit.algorithms import AlgorithmResult from qiskit.opflow import OperatorBase -class EvolutionResult: +class EvolutionResult(AlgorithmResult): """Class for holding evolution result and relevant metadata.""" - def __init__(self, evolved_object: OperatorBase, metadata: Optional[Dict[str, Any]] = None): + def __init__(self, evolved_state: OperatorBase = None, evolved_observable: OperatorBase = None): """ Args: - evolved_object: An evolved quantum state or an evolved quantum observable. - metadata: A dictionary with algorithm-specific metadata. Keys contain strings that name - data stores as a corresponding value. + evolved_state: An evolved quantum state; mutually exclusive with evolved_observable. + evolved_observable: An evolved quantum observable; mutually exclusive with + evolved_state. """ - self._evolved_object = evolved_object - self._metadata = metadata + self._evolved_state = evolved_state + self._evolved_observable = evolved_observable @property - def evolved_object(self) -> OperatorBase: - """Returns an evolved quantum state or an evolved quantum observable.""" - return self._evolved_object + def evolved_state(self) -> OperatorBase: + """Returns an evolved quantum state.""" + return self._evolved_state @property - def metadata(self) -> Union[Dict[str, Any], None]: - """Returns a dictionary with algorithm-specific metadata.""" - return self._metadata + def evolved_observable(self) -> OperatorBase: + """Returns an evolved quantum observable.""" + return self._evolved_observable From 05172e93c8f678a5a71024530678cf74fbfc3471 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 22 Feb 2022 17:16:33 +0100 Subject: [PATCH 038/145] Introduced evolution problem classes. --- .../time_evolution/evolution_base.py | 50 +++-------------- .../time_evolution/problems/__init__.py | 11 ++++ .../problems/evolution_problem.py | 48 ++++++++++++++++ .../problems/gradient_evolution_problem.py | 55 +++++++++++++++++++ 4 files changed, 123 insertions(+), 41 deletions(-) create mode 100644 qiskit/algorithms/time_evolution/problems/__init__.py create mode 100644 qiskit/algorithms/time_evolution/problems/evolution_problem.py create mode 100644 qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py diff --git a/qiskit/algorithms/time_evolution/evolution_base.py b/qiskit/algorithms/time_evolution/evolution_base.py index b796161f6625..974d3f9ace2e 100644 --- a/qiskit/algorithms/time_evolution/evolution_base.py +++ b/qiskit/algorithms/time_evolution/evolution_base.py @@ -13,37 +13,24 @@ """Base class for quantum time evolution.""" from abc import ABC, abstractmethod -from typing import Union, List, Optional, Dict +from qiskit.algorithms.time_evolution.problems.evolution_problem import EvolutionProblem from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult -from qiskit.circuit import Parameter -from qiskit.opflow import OperatorBase, StateFn, Gradient +from qiskit.algorithms.time_evolution.problems.gradient_evolution_problem import \ + GradientEvolutionProblem class EvolutionBase(ABC): """Base class for quantum time evolution.""" @abstractmethod - def evolve( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: Optional[StateFn] = None, - observable: Optional[OperatorBase] = None, - t_param: Optional[Parameter] = None, - hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, - ) -> EvolutionResult: + def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: """ Evolves an initial state or an observable according to a Hamiltonian provided. Args: - hamiltonian: Operator used for variational time evolution. - time: Total time of evolution. - initial_state: Quantum state to be evolved; mutually exclusive with observable. - observable: Observable to be evolved; mutually exclusive with initial_state. - t_param: Time parameter in case of a time-dependent Hamiltonian. - hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to certain - values, including the t_param. + evolution_problem: EvolutionProblem instance that includes definition of an evolution + problem. Returns: Evolution result which includes an evolved gradient of quantum state or an observable @@ -52,32 +39,13 @@ def evolve( raise NotImplementedError() @abstractmethod - def gradient( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn, - gradient_object: Gradient, - observable: Optional[OperatorBase] = None, - t_param: Optional[Parameter] = None, - hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, - gradient_params: Optional[List[Parameter]] = None, - ) -> EvolutionResult: + def gradient(self, gradient_evolution_problem: GradientEvolutionProblem) -> EvolutionResult: """ Performs Quantum Time Evolution of gradient expressions. Args: - hamiltonian: Operator used for variational time evolution. - time: Total time of evolution. - initial_state: Quantum state to be evolved. - gradient_object: Gradient object which defines a method for computing desired - gradients. - observable: Observable to be evolved. - t_param: Time parameter in case of a time-dependent Hamiltonian. - hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values, including the t_param. - gradient_params: List of parameters that indicates with respect to which parameters - gradients shall be computed. + gradient_evolution_problem: GradientEvolutionProblem instance that includes definition + of a gradient evolution problem. Returns: Evolution result which includes an evolved gradient of quantum state or an observable diff --git a/qiskit/algorithms/time_evolution/problems/__init__.py b/qiskit/algorithms/time_evolution/problems/__init__.py new file mode 100644 index 000000000000..fdb172d367f0 --- /dev/null +++ b/qiskit/algorithms/time_evolution/problems/__init__.py @@ -0,0 +1,11 @@ +# 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. diff --git a/qiskit/algorithms/time_evolution/problems/evolution_problem.py b/qiskit/algorithms/time_evolution/problems/evolution_problem.py new file mode 100644 index 000000000000..878fef4adcf5 --- /dev/null +++ b/qiskit/algorithms/time_evolution/problems/evolution_problem.py @@ -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. + +"""Evolution problem class.""" + +from typing import Union, Optional, Dict + +from qiskit.circuit import Parameter +from qiskit.opflow import OperatorBase, StateFn + + +class EvolutionProblem: + """Evolution problem class.""" + + def __init__( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: Optional[StateFn] = None, + observable: Optional[OperatorBase] = None, + t_param: Optional[Parameter] = None, + hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, + ): + """ + Args: + hamiltonian: Operator used for variational time evolution. + time: Total time of evolution. + initial_state: Quantum state to be evolved; mutually exclusive with observable. + observable: Observable to be evolved; mutually exclusive with initial_state. + t_param: Time parameter in case of a time-dependent Hamiltonian. + hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to certain + values, including the t_param. + """ + self.hamiltonian = hamiltonian + self.time = time + self.initial_state = initial_state + self.observable = observable + self.t_param = t_param + self.hamiltonian_value_dict = hamiltonian_value_dict diff --git a/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py b/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py new file mode 100644 index 000000000000..5104782b046c --- /dev/null +++ b/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py @@ -0,0 +1,55 @@ +# 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. + +"""Gradient evolution problem class.""" + +from typing import Union, List, Optional, Dict + +from qiskit.algorithms.time_evolution.problems.evolution_problem import EvolutionProblem +from qiskit.circuit import Parameter +from qiskit.opflow import OperatorBase, StateFn, Gradient + + +class GradientEvolutionProblem(EvolutionProblem): + """Gradient evolution problem class.""" + + def __init__( + self, + hamiltonian: OperatorBase, + time: float, + initial_state: StateFn, + gradient_object: Gradient, + observable: Optional[OperatorBase] = None, + t_param: Optional[Parameter] = None, + hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, + gradient_params: Optional[List[Parameter]] = None, + ): + """ + Args: + hamiltonian: Operator used for variational time evolution. + time: Total time of evolution. + initial_state: Quantum state to be evolved. + gradient_object: Gradient object which defines a method for computing desired + gradients. + observable: Observable to be evolved. + t_param: Time parameter in case of a time-dependent Hamiltonian. + hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to + certain values, including the t_param. + gradient_params: List of parameters that indicates with respect to which parameters + gradients shall be computed. + """ + super().__init__( + hamiltonian, time, initial_state, observable, t_param, hamiltonian_value_dict + ) + + self.gradient_object = gradient_object + self.gradient_params = gradient_params From 1ecf5b6d83b008a7cc15d61073af23fede4fc3b0 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 22 Feb 2022 17:18:38 +0100 Subject: [PATCH 039/145] Code refactoring. --- qiskit/algorithms/time_evolution/problems/evolution_problem.py | 2 +- .../time_evolution/problems/gradient_evolution_problem.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/algorithms/time_evolution/problems/evolution_problem.py b/qiskit/algorithms/time_evolution/problems/evolution_problem.py index 878fef4adcf5..b58a0d6afedf 100644 --- a/qiskit/algorithms/time_evolution/problems/evolution_problem.py +++ b/qiskit/algorithms/time_evolution/problems/evolution_problem.py @@ -32,7 +32,7 @@ def __init__( ): """ Args: - hamiltonian: Operator used for variational time evolution. + hamiltonian: The Hamiltonian under which to evolve the system. time: Total time of evolution. initial_state: Quantum state to be evolved; mutually exclusive with observable. observable: Observable to be evolved; mutually exclusive with initial_state. diff --git a/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py b/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py index 5104782b046c..ba28a6d43bde 100644 --- a/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py +++ b/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py @@ -35,7 +35,7 @@ def __init__( ): """ Args: - hamiltonian: Operator used for variational time evolution. + hamiltonian: The Hamiltonian under which to evolve the system. time: Total time of evolution. initial_state: Quantum state to be evolved. gradient_object: Gradient object which defines a method for computing desired From 8669094caf57dbcdb70da2bc32d69757cfad837b Mon Sep 17 00:00:00 2001 From: dlasecki Date: Tue, 22 Feb 2022 18:11:17 +0100 Subject: [PATCH 040/145] Apply suggestions from code review Co-authored-by: Julien Gacon --- qiskit/algorithms/time_evolution/evolution_result.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/algorithms/time_evolution/evolution_result.py b/qiskit/algorithms/time_evolution/evolution_result.py index 459aae519fa9..98f18434c219 100644 --- a/qiskit/algorithms/time_evolution/evolution_result.py +++ b/qiskit/algorithms/time_evolution/evolution_result.py @@ -18,7 +18,7 @@ class EvolutionResult(AlgorithmResult): """Class for holding evolution result and relevant metadata.""" - def __init__(self, evolved_state: OperatorBase = None, evolved_observable: OperatorBase = None): + def __init__(self, evolved_state: Optional[OperatorBase] = None, evolved_observable: Optional[OperatorBase] = None): """ Args: evolved_state: An evolved quantum state; mutually exclusive with evolved_observable. @@ -29,11 +29,11 @@ def __init__(self, evolved_state: OperatorBase = None, evolved_observable: Opera self._evolved_observable = evolved_observable @property - def evolved_state(self) -> OperatorBase: + def evolved_state(self) -> Optional[OperatorBase]: """Returns an evolved quantum state.""" return self._evolved_state @property - def evolved_observable(self) -> OperatorBase: + def evolved_observable(self) -> Optional[OperatorBase]: """Returns an evolved quantum observable.""" return self._evolved_observable From f55d38f317a10bdb5171dbbced0cbd72299d7fb0 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 23 Feb 2022 12:56:34 +0100 Subject: [PATCH 041/145] Added unit tests. --- .../time_evolution/evolution_base.py | 5 +- .../time_evolution/evolution_result.py | 21 ++-- .../problems/evolution_problem.py | 7 ++ .../problems/gradient_evolution_problem.py | 4 +- .../algorithms/time_evolution/__init__.py | 11 ++ .../time_evolution/problems/__init__.py | 11 ++ .../problems/test_evolution_problem.py | 84 ++++++++++++++ .../test_gradient_evolution_problem.py | 106 ++++++++++++++++++ .../time_evolution/test_evolution_result.py | 42 +++++++ 9 files changed, 274 insertions(+), 17 deletions(-) create mode 100644 test/python/algorithms/time_evolution/__init__.py create mode 100644 test/python/algorithms/time_evolution/problems/__init__.py create mode 100644 test/python/algorithms/time_evolution/problems/test_evolution_problem.py create mode 100644 test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py create mode 100644 test/python/algorithms/time_evolution/test_evolution_result.py diff --git a/qiskit/algorithms/time_evolution/evolution_base.py b/qiskit/algorithms/time_evolution/evolution_base.py index 974d3f9ace2e..35929ad7fad9 100644 --- a/qiskit/algorithms/time_evolution/evolution_base.py +++ b/qiskit/algorithms/time_evolution/evolution_base.py @@ -16,8 +16,9 @@ from qiskit.algorithms.time_evolution.problems.evolution_problem import EvolutionProblem from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult -from qiskit.algorithms.time_evolution.problems.gradient_evolution_problem import \ - GradientEvolutionProblem +from qiskit.algorithms.time_evolution.problems.gradient_evolution_problem import ( + GradientEvolutionProblem, +) class EvolutionBase(ABC): diff --git a/qiskit/algorithms/time_evolution/evolution_result.py b/qiskit/algorithms/time_evolution/evolution_result.py index 98f18434c219..ae82a549aae7 100644 --- a/qiskit/algorithms/time_evolution/evolution_result.py +++ b/qiskit/algorithms/time_evolution/evolution_result.py @@ -10,6 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """Class for holding evolution result and relevant metadata.""" +from typing import Optional from qiskit.algorithms import AlgorithmResult from qiskit.opflow import OperatorBase @@ -18,22 +19,16 @@ class EvolutionResult(AlgorithmResult): """Class for holding evolution result and relevant metadata.""" - def __init__(self, evolved_state: Optional[OperatorBase] = None, evolved_observable: Optional[OperatorBase] = None): + def __init__( + self, + evolved_state: Optional[OperatorBase] = None, + evolved_observable: Optional[OperatorBase] = None, + ): """ Args: evolved_state: An evolved quantum state; mutually exclusive with evolved_observable. evolved_observable: An evolved quantum observable; mutually exclusive with evolved_state. """ - self._evolved_state = evolved_state - self._evolved_observable = evolved_observable - - @property - def evolved_state(self) -> Optional[OperatorBase]: - """Returns an evolved quantum state.""" - return self._evolved_state - - @property - def evolved_observable(self) -> Optional[OperatorBase]: - """Returns an evolved quantum observable.""" - return self._evolved_observable + self.evolved_state = evolved_state + self.evolved_observable = evolved_observable diff --git a/qiskit/algorithms/time_evolution/problems/evolution_problem.py b/qiskit/algorithms/time_evolution/problems/evolution_problem.py index b58a0d6afedf..c9a25e34c08c 100644 --- a/qiskit/algorithms/time_evolution/problems/evolution_problem.py +++ b/qiskit/algorithms/time_evolution/problems/evolution_problem.py @@ -39,7 +39,14 @@ def __init__( t_param: Time parameter in case of a time-dependent Hamiltonian. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to certain values, including the t_param. + + Raises: + ValueError: if both initial_state and observable are provided. """ + + if initial_state is not None and observable is not None: + raise ValueError("initial_state and observable are mutually exclusive; both provided.") + self.hamiltonian = hamiltonian self.time = time self.initial_state = initial_state diff --git a/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py b/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py index ba28a6d43bde..9ccb8ff226ee 100644 --- a/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py +++ b/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py @@ -26,8 +26,8 @@ def __init__( self, hamiltonian: OperatorBase, time: float, - initial_state: StateFn, gradient_object: Gradient, + initial_state: Optional[StateFn] = None, observable: Optional[OperatorBase] = None, t_param: Optional[Parameter] = None, hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, @@ -37,9 +37,9 @@ def __init__( Args: hamiltonian: The Hamiltonian under which to evolve the system. time: Total time of evolution. - initial_state: Quantum state to be evolved. gradient_object: Gradient object which defines a method for computing desired gradients. + initial_state: Quantum state to be evolved. observable: Observable to be evolved. t_param: Time parameter in case of a time-dependent Hamiltonian. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to diff --git a/test/python/algorithms/time_evolution/__init__.py b/test/python/algorithms/time_evolution/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/test/python/algorithms/time_evolution/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/test/python/algorithms/time_evolution/problems/__init__.py b/test/python/algorithms/time_evolution/problems/__init__.py new file mode 100644 index 000000000000..96c0cf22bec9 --- /dev/null +++ b/test/python/algorithms/time_evolution/problems/__init__.py @@ -0,0 +1,11 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. diff --git a/test/python/algorithms/time_evolution/problems/test_evolution_problem.py b/test/python/algorithms/time_evolution/problems/test_evolution_problem.py new file mode 100644 index 000000000000..486785a0a837 --- /dev/null +++ b/test/python/algorithms/time_evolution/problems/test_evolution_problem.py @@ -0,0 +1,84 @@ +# 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. + +"""Test evolution problem class.""" + +from qiskit.algorithms.time_evolution.problems.evolution_problem import EvolutionProblem +from qiskit.circuit import Parameter +from qiskit.opflow import Y, Z, One +from test.python.algorithms import QiskitAlgorithmsTestCase + + +class TestEvolutionProblem(QiskitAlgorithmsTestCase): + """Test evolution problem class.""" + + def test_init_default(self): + """Tests that all default fields are initialized correctly.""" + hamiltonian = Y + time = 2.5 + + evo_problem = EvolutionProblem(hamiltonian, time) + + expected_hamiltonian = Y + expected_time = 2.5 + expected_initial_state = None + expected_observable = None + expected_t_param = None + expected_hamiltonian_value_dict = None + + self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) + self.assertEqual(evo_problem.time, expected_time) + self.assertEqual(evo_problem.initial_state, expected_initial_state) + self.assertEqual(evo_problem.observable, expected_observable) + self.assertEqual(evo_problem.t_param, expected_t_param) + self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) + + def test_init_all(self): + """Tests that all fields are initialized correctly.""" + t_parameter = Parameter("t") + hamiltonian = t_parameter * Z + Y + time = 2 + initial_state = One + observable = None + hamiltonian_value_dict = {t_parameter: 3.2} + + evo_problem = EvolutionProblem( + hamiltonian, time, initial_state, observable, t_parameter, hamiltonian_value_dict + ) + + expected_hamiltonian = Y + t_parameter * Z + expected_time = 2 + expected_initial_state = One + expected_observable = None + expected_t_param = t_parameter + expected_hamiltonian_value_dict = {t_parameter: 3.2} + + self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) + self.assertEqual(evo_problem.time, expected_time) + self.assertEqual(evo_problem.initial_state, expected_initial_state) + self.assertEqual(evo_problem.observable, expected_observable) + self.assertEqual(evo_problem.t_param, expected_t_param) + self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) + + def test_init_error(self): + """Tests that an error is raised when both initial_state and observable provided.""" + t_parameter = Parameter("t") + hamiltonian = t_parameter * Z + Y + time = 2 + initial_state = One + observable = Y + hamiltonian_value_dict = {t_parameter: 3.2} + + with self.assertRaises(ValueError): + _ = EvolutionProblem( + hamiltonian, time, initial_state, observable, t_parameter, hamiltonian_value_dict + ) diff --git a/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py b/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py new file mode 100644 index 000000000000..ec2f02e914dc --- /dev/null +++ b/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py @@ -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. + +"""Test gradient evolution problem class.""" + +from qiskit.algorithms.time_evolution.problems.gradient_evolution_problem import ( + GradientEvolutionProblem, +) +from qiskit.circuit import Parameter +from qiskit.opflow import Y, Z, One, Gradient +from test.python.algorithms import QiskitAlgorithmsTestCase + + +class TestGradientEvolutionProblem(QiskitAlgorithmsTestCase): + """Test gradient evolution problem class.""" + + def test_init_default(self): + """Tests that all default fields are initialized correctly.""" + hamiltonian = Y + time = 2.5 + gradient_object = Gradient() + + evo_problem = GradientEvolutionProblem(hamiltonian, time, gradient_object) + + expected_hamiltonian = Y + expected_time = 2.5 + expected_gradient_object = gradient_object + expected_initial_state = None + expected_observable = None + expected_t_param = None + expected_hamiltonian_value_dict = None + expected_gradient_params = None + + self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) + self.assertEqual(evo_problem.time, expected_time) + self.assertEqual(evo_problem.gradient_object, expected_gradient_object) + self.assertEqual(evo_problem.initial_state, expected_initial_state) + self.assertEqual(evo_problem.observable, expected_observable) + self.assertEqual(evo_problem.t_param, expected_t_param) + self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) + self.assertEqual(evo_problem.gradient_params, expected_gradient_params) + + def test_init_all(self): + """Tests that all fields are initialized correctly.""" + t_parameter = Parameter("t") + param = Parameter("x") + hamiltonian = t_parameter * Z + param * Y + time = 2 + gradient_object = Gradient() + + initial_state = One + observable = None + hamiltonian_value_dict = {t_parameter: 3.2} + gradient_params = [param] + + evo_problem = GradientEvolutionProblem( + hamiltonian, + time, + gradient_object, + initial_state, + observable, + t_parameter, + hamiltonian_value_dict, + gradient_params, + ) + + expected_hamiltonian = param * Y + t_parameter * Z + expected_time = 2 + expected_gradient_object = gradient_object + expected_initial_state = One + expected_observable = None + expected_t_param = t_parameter + expected_hamiltonian_value_dict = {t_parameter: 3.2} + expected_gradient_params = [param] + + self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) + self.assertEqual(evo_problem.time, expected_time) + self.assertEqual(evo_problem.gradient_object, expected_gradient_object) + self.assertEqual(evo_problem.initial_state, expected_initial_state) + self.assertEqual(evo_problem.observable, expected_observable) + self.assertEqual(evo_problem.t_param, expected_t_param) + self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) + self.assertEqual(evo_problem.gradient_params, expected_gradient_params) + + def test_init_error(self): + """Tests that an error is raised when both initial_state and observable provided.""" + t_parameter = Parameter("t") + hamiltonian = t_parameter * Z + Y + time = 2 + gradient_object = Gradient() + initial_state = One + observable = Y + + with self.assertRaises(ValueError): + _ = GradientEvolutionProblem( + hamiltonian, time, gradient_object, initial_state, observable + ) diff --git a/test/python/algorithms/time_evolution/test_evolution_result.py b/test/python/algorithms/time_evolution/test_evolution_result.py new file mode 100644 index 000000000000..346c7cc961a1 --- /dev/null +++ b/test/python/algorithms/time_evolution/test_evolution_result.py @@ -0,0 +1,42 @@ +# 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. +"""Class for testing evolution result.""" + +from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult +from qiskit.opflow import Zero, X +from test.python.algorithms import QiskitAlgorithmsTestCase + + +class TestEvolutionResult(QiskitAlgorithmsTestCase): + """Class for testing evolution result and relevant metadata.""" + + def test_init_state(self): + """Tests that a class is initialized correctly with an evolved_state.""" + evolved_state = Zero + evo_result = EvolutionResult(evolved_state=evolved_state) + + expected_state = Zero + expected_observable = None + + self.assertEqual(evo_result.evolved_state, expected_state) + self.assertEqual(evo_result.evolved_observable, expected_observable) + + def test_init_observable(self): + """Tests that a class is initialized correctly with an evolved_observable.""" + evolved_observable = X + evo_result = EvolutionResult(evolved_observable=evolved_observable) + + expected_state = None + expected_observable = X + + self.assertEqual(evo_result.evolved_state, expected_state) + self.assertEqual(evo_result.evolved_observable, expected_observable) From 27c69ae9a39ef2de128a01146b8ec1b7decb88bb Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 23 Feb 2022 14:15:51 +0100 Subject: [PATCH 042/145] Lint fixed. --- .../time_evolution/problems/test_evolution_problem.py | 2 +- .../time_evolution/problems/test_gradient_evolution_problem.py | 2 +- test/python/algorithms/time_evolution/test_evolution_result.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/python/algorithms/time_evolution/problems/test_evolution_problem.py b/test/python/algorithms/time_evolution/problems/test_evolution_problem.py index 486785a0a837..2bd3f3fac2d6 100644 --- a/test/python/algorithms/time_evolution/problems/test_evolution_problem.py +++ b/test/python/algorithms/time_evolution/problems/test_evolution_problem.py @@ -12,10 +12,10 @@ """Test evolution problem class.""" +from test.python.algorithms import QiskitAlgorithmsTestCase from qiskit.algorithms.time_evolution.problems.evolution_problem import EvolutionProblem from qiskit.circuit import Parameter from qiskit.opflow import Y, Z, One -from test.python.algorithms import QiskitAlgorithmsTestCase class TestEvolutionProblem(QiskitAlgorithmsTestCase): diff --git a/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py b/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py index ec2f02e914dc..2e00dcc78cc0 100644 --- a/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py +++ b/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py @@ -12,12 +12,12 @@ """Test gradient evolution problem class.""" +from test.python.algorithms import QiskitAlgorithmsTestCase from qiskit.algorithms.time_evolution.problems.gradient_evolution_problem import ( GradientEvolutionProblem, ) from qiskit.circuit import Parameter from qiskit.opflow import Y, Z, One, Gradient -from test.python.algorithms import QiskitAlgorithmsTestCase class TestGradientEvolutionProblem(QiskitAlgorithmsTestCase): diff --git a/test/python/algorithms/time_evolution/test_evolution_result.py b/test/python/algorithms/time_evolution/test_evolution_result.py index 346c7cc961a1..2d8d40e3dd1d 100644 --- a/test/python/algorithms/time_evolution/test_evolution_result.py +++ b/test/python/algorithms/time_evolution/test_evolution_result.py @@ -11,9 +11,9 @@ # that they have been altered from the originals. """Class for testing evolution result.""" +from test.python.algorithms import QiskitAlgorithmsTestCase from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult from qiskit.opflow import Zero, X -from test.python.algorithms import QiskitAlgorithmsTestCase class TestEvolutionResult(QiskitAlgorithmsTestCase): From df0cbf4575b66812a5a87ed7a5fc8b78bfa2a914 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 28 Feb 2022 08:15:23 +0100 Subject: [PATCH 043/145] Code refactoring --- .../trotterization/trotter_ops_validator.py | 15 +++++- .../trotterization/trotter_qrte.py | 51 +++++++++++-------- qiskit/test/base.py | 1 - 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py index faed35320213..9b27f71de02d 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py @@ -26,8 +26,10 @@ def _is_op_bound(operator: Union[SummedOp, PauliOp, OperatorBase]) -> None: """Checks if an operator provided has all parameters bound. + Args: operator: Operator to be checked. + Raises: ValueError: If an operator has unbound parameters. """ @@ -39,7 +41,13 @@ def _is_op_bound(operator: Union[SummedOp, PauliOp, OperatorBase]) -> None: def _validate_input(initial_state: StateFn, observable: OperatorBase) -> None: - """Validates if one and only one among initial_state and observable is provided.""" + """ + Validates if one and only one among initial_state and observable is provided. + + Args: + initial_state: A variable potentially holding a quantum state. + observable: A variable potentially holding a quantum observable. + """ if initial_state is None and observable is None: raise ValueError( "TrotterQrte requires an initial state or an observable to be evolved; None " @@ -55,8 +63,10 @@ def _validate_input(initial_state: StateFn, observable: OperatorBase) -> None: def _validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBase]): """Validates that a Hamiltonian is of a correct type and with expected dependence on parameters. + Args: hamiltonian: Hamiltonian to be validated. + Raises: ValueError: if an invalid Hamiltonian is provided. """ @@ -84,11 +94,14 @@ def _validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBas def _is_linear_with_single_param(operator: OperatorBase) -> bool: """Checks if an operator provided is linear w.r.t. one and only one parameter. + Args: operator: Operator to be checked. + Returns: True or False depending on whether an operator is linear in a single param and only contains' a single param. + Raises: ValueError: If an operator contains more than 1 parameter. """ diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index 961e7cbbd16e..d9a0922d7612 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -16,7 +16,8 @@ from itertools import product from typing import Union, List, Dict, Optional -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_ops_validator import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.\ + trotter_ops_validator import ( _validate_hamiltonian_form, _validate_input, _is_op_bound, @@ -58,7 +59,7 @@ class TrotterQrte(Qrte): evolved_state = trotter_qrte.evolve(operator, time, initial_state) """ - def __init__(self, product_formula: ProductFormula = LieTrotter()): + def __init__(self, product_formula: ProductFormula = LieTrotter()) -> None: """ Args: product_formula: A Lie-Trotter-Suzuki product formula. The default is the Lie-Trotter @@ -79,6 +80,7 @@ def evolve( Evolves a quantum state or an observable for a given time using the Trotterization method based on a product formula provided. Time-dependent Hamiltonians are not yet supported. + Args: hamiltonian: The operator to evolve. Can also be provided as list of non-commuting @@ -86,12 +88,12 @@ def evolve( For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. time: Total time of evolution. initial_state: If interested in a quantum state time evolution, a quantum state to be - evolved. + evolved. observable: If interested in a quantum observable time evolution, a quantum observable - to be evolved. + to be evolved. t_param: Not supported by this algorithm. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values. + certain values. Returns: The evolved hamiltonian applied to either an initial state or an observable. @@ -130,13 +132,14 @@ def _try_binding_params( ) -> Union[SummedOp, PauliOp, OperatorBase]: """ Tries binding parameters in a Hamiltonian. + Args: hamiltonian: The operator to evolve. Can also be provided as list of non-commuting operators where the elements are sums of commuting operators. For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values. + certain values. Returns: Bound Hamiltonian. @@ -186,20 +189,21 @@ def gradient( """ Computes gradients of a quantum state evolved according to a given Hamiltonian and w.r.t. observable and parameters provided. + Args: hamiltonian: The operator to evolve. Can also be provided as list of non-commuting - operators where the elements are sums of commuting operators. - For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. + operators where the elements are sums of commuting operators. + For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. time: Total time of evolution. initial_state: If interested in a quantum state time evolution, a quantum state to be - evolved. + evolved. gradient_object: TrotterQrte does not support custom Gradient method. Provided Gradient - object is ignored. + object is ignored. observable: If interested in a quantum observable time evolution, a quantum observable - to be evolved. + to be evolved. t_param: Not supported by this algorithm. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values. + certain values. gradient_params: Parameters w.r.t. which gradients shall be computed. Returns: @@ -312,16 +316,17 @@ def _calc_time_gradient_finite_diff( ) -> float: """Calculates a gradient using the finite difference method. It is used for gradients w.r.t. a potential time parameter. + Args: hamiltonian: The operator to evolve. Can also be provided as list of non-commuting - operators where the elements are sums of commuting operators. - For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. + operators where the elements are sums of commuting operators. + For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values. + certain values. initial_state: If interested in a quantum state time evolution, a quantum state to be - evolved. + evolved. observable: If interested in a quantum observable time evolution, a quantum observable - to be evolved. + to be evolved. t_param: Not supported by this algorithm. time: Total time of evolution. epsilon: Size of a finite difference interval. @@ -349,19 +354,21 @@ def _calc_term_gradient( ) -> float: """Calculates a gradient of a Hamiltonian term (a single summand) with respect to parameters given. + Args: hamiltonian: The operator to evolve. Can also be provided as list of non-commuting - operators where the elements are sums of commuting operators. - For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. + operators where the elements are sums of commuting operators. + For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. hamiltonian_term: One of the terms (summands) from a Hamiltonian. initial_state: If interested in a quantum state time evolution, a quantum state to be - evolved. + evolved. observable: If interested in a quantum observable time evolution, a quantum observable - to be evolved. + to be evolved. t_param: Not supported by this algorithm. time: Total time of evolution. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values. + certain values. + Returns: Gradient of an evolved state w.r.t. parameters from a hamiltonian_value_dict. """ diff --git a/qiskit/test/base.py b/qiskit/test/base.py index e6814a89d1e7..7cbd80d73431 100644 --- a/qiskit/test/base.py +++ b/qiskit/test/base.py @@ -62,7 +62,6 @@ class BaseTestCase(testtools.TestCase): assertRaises = unittest.TestCase.assertRaises assertEqual = unittest.TestCase.assertEqual - else: class BaseTestCase(unittest.TestCase): From 43e0084138512d027875e922d83e29d6822bd633 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 28 Feb 2022 14:05:00 +0100 Subject: [PATCH 044/145] Code refactoring --- .../time_evolution/evolution_base.py | 18 --------- .../time_evolution/evolution_gradient_base.py | 40 +++++++++++++++++++ .../time_evolution/imaginary/qite.py | 2 +- qiskit/algorithms/time_evolution/real/qrte.py | 2 +- 4 files changed, 42 insertions(+), 20 deletions(-) create mode 100644 qiskit/algorithms/time_evolution/evolution_gradient_base.py diff --git a/qiskit/algorithms/time_evolution/evolution_base.py b/qiskit/algorithms/time_evolution/evolution_base.py index 35929ad7fad9..aa698376db0c 100644 --- a/qiskit/algorithms/time_evolution/evolution_base.py +++ b/qiskit/algorithms/time_evolution/evolution_base.py @@ -16,9 +16,6 @@ from qiskit.algorithms.time_evolution.problems.evolution_problem import EvolutionProblem from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult -from qiskit.algorithms.time_evolution.problems.gradient_evolution_problem import ( - GradientEvolutionProblem, -) class EvolutionBase(ABC): @@ -38,18 +35,3 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: and metadata. """ raise NotImplementedError() - - @abstractmethod - def gradient(self, gradient_evolution_problem: GradientEvolutionProblem) -> EvolutionResult: - """ - Performs Quantum Time Evolution of gradient expressions. - - Args: - gradient_evolution_problem: GradientEvolutionProblem instance that includes definition - of a gradient evolution problem. - - Returns: - Evolution result which includes an evolved gradient of quantum state or an observable - and metadata. - """ - raise NotImplementedError() diff --git a/qiskit/algorithms/time_evolution/evolution_gradient_base.py b/qiskit/algorithms/time_evolution/evolution_gradient_base.py new file mode 100644 index 000000000000..283a995f177e --- /dev/null +++ b/qiskit/algorithms/time_evolution/evolution_gradient_base.py @@ -0,0 +1,40 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021, 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. + +"""Base class for quantum time evolution and gradient evolution.""" + +from abc import ABC, abstractmethod + +from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult +from qiskit.algorithms.time_evolution.problems.gradient_evolution_problem import ( + GradientEvolutionProblem, +) +from qiskit.opflow import EvolutionBase + + +class GradientEvolutionBase(ABC, EvolutionBase): + """Base class for quantum time evolution and gradient evolution.""" + + @abstractmethod + def gradient(self, gradient_evolution_problem: GradientEvolutionProblem) -> EvolutionResult: + """ + Performs Quantum Time Evolution of gradient expressions. + + Args: + gradient_evolution_problem: GradientEvolutionProblem instance that includes definition + of a gradient evolution problem. + + Returns: + Evolution result which includes an evolved gradient of quantum state or an observable + and metadata. + """ + raise NotImplementedError() diff --git a/qiskit/algorithms/time_evolution/imaginary/qite.py b/qiskit/algorithms/time_evolution/imaginary/qite.py index 7dd8006607c0..4ce093618ab0 100644 --- a/qiskit/algorithms/time_evolution/imaginary/qite.py +++ b/qiskit/algorithms/time_evolution/imaginary/qite.py @@ -17,5 +17,5 @@ from qiskit.algorithms.time_evolution.evolution_base import EvolutionBase -class QITE(EvolutionBase, ABC): +class QITE(ABC): """Interface for Quantum Imaginary Time Evolution.""" diff --git a/qiskit/algorithms/time_evolution/real/qrte.py b/qiskit/algorithms/time_evolution/real/qrte.py index 54f85c9b955b..1c5946b9b787 100644 --- a/qiskit/algorithms/time_evolution/real/qrte.py +++ b/qiskit/algorithms/time_evolution/real/qrte.py @@ -17,5 +17,5 @@ from qiskit.algorithms.time_evolution.evolution_base import EvolutionBase -class QRTE(EvolutionBase, ABC): +class QRTE(ABC): """Base class for quantum real time evolution.""" From ea83dd02ab2cc8fb83cdf9b784fb93ff62b58b4c Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 28 Feb 2022 15:14:54 +0100 Subject: [PATCH 045/145] Code refactoring --- qiskit/algorithms/time_evolution/evolution_gradient_base.py | 2 +- qiskit/algorithms/time_evolution/imaginary/qite.py | 2 -- qiskit/algorithms/time_evolution/real/qrte.py | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/qiskit/algorithms/time_evolution/evolution_gradient_base.py b/qiskit/algorithms/time_evolution/evolution_gradient_base.py index 283a995f177e..ec6f6b94de90 100644 --- a/qiskit/algorithms/time_evolution/evolution_gradient_base.py +++ b/qiskit/algorithms/time_evolution/evolution_gradient_base.py @@ -21,7 +21,7 @@ from qiskit.opflow import EvolutionBase -class GradientEvolutionBase(ABC, EvolutionBase): +class GradientEvolutionBase(EvolutionBase, ABC): """Base class for quantum time evolution and gradient evolution.""" @abstractmethod diff --git a/qiskit/algorithms/time_evolution/imaginary/qite.py b/qiskit/algorithms/time_evolution/imaginary/qite.py index 4ce093618ab0..5a2bb20b75c8 100644 --- a/qiskit/algorithms/time_evolution/imaginary/qite.py +++ b/qiskit/algorithms/time_evolution/imaginary/qite.py @@ -14,8 +14,6 @@ from abc import ABC -from qiskit.algorithms.time_evolution.evolution_base import EvolutionBase - class QITE(ABC): """Interface for Quantum Imaginary Time Evolution.""" diff --git a/qiskit/algorithms/time_evolution/real/qrte.py b/qiskit/algorithms/time_evolution/real/qrte.py index 1c5946b9b787..a8681710f6ab 100644 --- a/qiskit/algorithms/time_evolution/real/qrte.py +++ b/qiskit/algorithms/time_evolution/real/qrte.py @@ -14,8 +14,6 @@ from abc import ABC -from qiskit.algorithms.time_evolution.evolution_base import EvolutionBase - class QRTE(ABC): """Base class for quantum real time evolution.""" From 207a2caee00a3c0596e1ce320a8068179ffdf22b Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 28 Feb 2022 16:15:39 +0100 Subject: [PATCH 046/145] Code refactoring --- .../trotterization/trotter_ops_validator.py | 51 +++++++++++++++---- .../trotterization/trotter_qrte.py | 3 +- .../test_trotter_ops_validator.py | 25 +++++++++ 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py index 9b27f71de02d..a9a44d1656b6 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py @@ -77,22 +77,22 @@ def _validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBas "ParameterExpression." ) for op in hamiltonian.oplist: - if not _is_linear_with_single_param(op): + if not _is_pauli_linear_with_single_param(op): raise ValueError( "Hamiltonian term has a coefficient that is not a linear function of a " "single parameter. It is not supported." ) elif isinstance(hamiltonian, (PauliOp, OperatorBase)): - if not _is_linear_with_single_param(hamiltonian): + if not _is_pauli_linear_with_single_param(hamiltonian): raise ValueError( "Hamiltonian term has a coefficient that is not a linear function of a " "single parameter. It is not supported." ) else: - raise ValueError("Hamiltonian not a SummedOp which is the only option supported.") + raise ValueError("Hamiltonian not a SummedOp/PauliOp which are the only options supported.") -def _is_linear_with_single_param(operator: OperatorBase) -> bool: +def _is_pauli_linear_with_single_param(operator: PauliOp) -> bool: """Checks if an operator provided is linear w.r.t. one and only one parameter. Args: @@ -105,18 +105,49 @@ def _is_linear_with_single_param(operator: OperatorBase) -> bool: Raises: ValueError: If an operator contains more than 1 parameter. """ - if ( - not isinstance(operator.coeff, ParameterExpression) - and not isinstance(operator.coeff, Parameter) - or len(operator.coeff.parameters) == 0 - ): + if not isinstance(operator, PauliOp): + raise ValueError(f"Only PauliOp expected. {type(operator)} provided") + if _is_operator_no_parameters(operator): return True if len(operator.coeff.parameters) > 1: raise ValueError( "Term of a Hamiltonian has a coefficient that depends on several " "parameters. Only dependence on a single parameter is allowed." ) + gradient = _operator_derivative(operator) + return isinstance(gradient, numbers.Number) + + +def _operator_derivative( + operator: PauliOp, +) -> Union[numbers.Number, Parameter, ParameterExpression]: + """ + Calculates the gradient of an operator coefficient. + + Args: + operator: PauliOp. + + Returns: + Gradient of a PauliOp coefficient. + """ single_parameter_expression = operator.coeff parameter = list(single_parameter_expression.parameters)[0] gradient = single_parameter_expression.gradient(parameter) - return isinstance(gradient, numbers.Number) + return gradient + + +def _is_operator_no_parameters(operator: PauliOp) -> bool: + """ + Checks if an operator is parametrized. + + Args: + operator: PauliOp. + + Returns: + Boolean flag indicating whether the PauliOp is parametrized or not. + """ + return ( + not isinstance(operator.coeff, ParameterExpression) + and not isinstance(operator.coeff, Parameter) + or len(operator.coeff.parameters) == 0 + ) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index d9a0922d7612..ae4edd8af694 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -16,8 +16,7 @@ from itertools import product from typing import Union, List, Dict, Optional -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.\ - trotter_ops_validator import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_ops_validator import ( _validate_hamiltonian_form, _validate_input, _is_op_bound, diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py index 0a82931766c0..6d9c55a5cd9c 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py @@ -19,7 +19,9 @@ from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_ops_validator import ( _validate_hamiltonian_form, + _is_pauli_linear_with_single_param, ) +from qiskit.circuit.library import EfficientSU2 from test.python.opflow import QiskitOpflowTestCase from qiskit.circuit import Parameter from qiskit.opflow import ( @@ -38,7 +40,10 @@ class TestTrotterQrte(QiskitOpflowTestCase): (Parameter("theta") * Y, True), (Parameter("theta") * Parameter("gamma") * Z, False), (Parameter("theta") * X + Parameter("gamma") * Y, True), + (Parameter("theta") * X + Y, True), + (X + Parameter("gamma") * Y, True), (Parameter("theta1") * Parameter("theta2") * X + Parameter("gamma") * Y, False), + (EfficientSU2, False), ) @unpack def test_validate_hamiltonian_form(self, hamiltonian, expected): @@ -50,6 +55,26 @@ def test_validate_hamiltonian_form(self, hamiltonian, expected): np.testing.assert_equal(valid, expected) + @data( + (X, True), + (X + Y, False), + (-5 * X, True), + (5j * Y, True), + (X + Parameter("theta1") * Parameter("theta2") * Y, False), + (Parameter("theta") * Y, True), + (Parameter("theta") * Parameter("gamma") * Z, False), + (5 * Parameter("theta") * X, True), + (Parameter("theta1") * Parameter("theta2") * X, False), + ) + @unpack + def test_is_pauli_linear_with_single_param(self, operator, expected): + try: + linear_with_single_param = _is_pauli_linear_with_single_param(operator) + except ValueError: + linear_with_single_param = False + + np.testing.assert_equal(linear_with_single_param, expected) + if __name__ == "__main__": unittest.main() From 79c893e3ef41cb1d5eff144fa9e702a933c572ee Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 1 Mar 2022 11:19:02 +0100 Subject: [PATCH 047/145] Code refactoring --- .../trotterization/trotter_ops_validator.py | 9 ++++++--- .../test_trotter_ops_validator.py | 10 ++++++---- .../trotterization/test_trotter_qrte.py | 17 +++++++---------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py index a9a44d1656b6..6b76418df6fe 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py @@ -47,6 +47,9 @@ def _validate_input(initial_state: StateFn, observable: OperatorBase) -> None: Args: initial_state: A variable potentially holding a quantum state. observable: A variable potentially holding a quantum observable. + + Raises: + ValueError: If both initial_state and observable are provided or none of them. """ if initial_state is None and observable is None: raise ValueError( @@ -77,13 +80,13 @@ def _validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBas "ParameterExpression." ) for op in hamiltonian.oplist: - if not _is_pauli_linear_with_single_param(op): + if not _is_pauli_lin_single_param(op): raise ValueError( "Hamiltonian term has a coefficient that is not a linear function of a " "single parameter. It is not supported." ) elif isinstance(hamiltonian, (PauliOp, OperatorBase)): - if not _is_pauli_linear_with_single_param(hamiltonian): + if not _is_pauli_lin_single_param(hamiltonian): raise ValueError( "Hamiltonian term has a coefficient that is not a linear function of a " "single parameter. It is not supported." @@ -92,7 +95,7 @@ def _validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBas raise ValueError("Hamiltonian not a SummedOp/PauliOp which are the only options supported.") -def _is_pauli_linear_with_single_param(operator: PauliOp) -> bool: +def _is_pauli_lin_single_param(operator: PauliOp) -> bool: """Checks if an operator provided is linear w.r.t. one and only one parameter. Args: diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py index 6d9c55a5cd9c..a618c9f226b7 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py @@ -14,15 +14,15 @@ import unittest +from test.python.opflow import QiskitOpflowTestCase from ddt import ddt, data, unpack import numpy as np from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_ops_validator import ( _validate_hamiltonian_form, - _is_pauli_linear_with_single_param, + _is_pauli_lin_single_param, ) from qiskit.circuit.library import EfficientSU2 -from test.python.opflow import QiskitOpflowTestCase from qiskit.circuit import Parameter from qiskit.opflow import ( X, @@ -47,6 +47,7 @@ class TestTrotterQrte(QiskitOpflowTestCase): ) @unpack def test_validate_hamiltonian_form(self, hamiltonian, expected): + """Tests that a Hamiltonian is of a valid form supported by the TrotterQrte algorithm.""" valid = True try: _validate_hamiltonian_form(hamiltonian) @@ -67,9 +68,10 @@ def test_validate_hamiltonian_form(self, hamiltonian, expected): (Parameter("theta1") * Parameter("theta2") * X, False), ) @unpack - def test_is_pauli_linear_with_single_param(self, operator, expected): + def test_is_pauli_lin_single_param(self, operator, expected): + """Tests that operator is validated to be linearly dependent on a single parameter.""" try: - linear_with_single_param = _is_pauli_linear_with_single_param(operator) + linear_with_single_param = _is_pauli_lin_single_param(operator) except ValueError: linear_with_single_param = False diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index c73e8fbbcd95..2b5ac14468c2 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -14,12 +14,12 @@ import unittest +from test.python.opflow import QiskitOpflowTestCase from ddt import ddt, data import numpy as np from numpy.testing import assert_raises from scipy.linalg import expm -from test.python.opflow import QiskitOpflowTestCase from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( TrotterQrte, ) @@ -188,8 +188,8 @@ def test_trotter_qrte_gradient_summed_op_qdrift_2(self): ) expected_gradient = {theta1: 0j, theta2: 0j} np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) - for key in expected_gradient.keys(): - np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) + for key, expected_gradient_value in expected_gradient.items(): + np.testing.assert_almost_equal(gradient[key], expected_gradient_value) def test_trotter_qrte_gradient_summed_op_qdrift_3(self): """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" @@ -214,8 +214,8 @@ def test_trotter_qrte_gradient_summed_op_qdrift_3(self): ) expected_gradient = {theta1: 0j, theta2: 0j} np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) - for key in expected_gradient.keys(): - np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) + for key, expected_gradient_value in expected_gradient.items(): + np.testing.assert_almost_equal(gradient[key], expected_gradient_value) # TODO fix problem with randomness causing different results def test_trotter_qrte_gradient_summed_op_qdrift_4(self): @@ -242,8 +242,8 @@ def test_trotter_qrte_gradient_summed_op_qdrift_4(self): ) expected_gradient = {theta1: 10.938002663771012 + 0j, theta2: 9.745099298494452 + 0j} np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) - for key in expected_gradient.keys(): - np.testing.assert_almost_equal(gradient[key], expected_gradient[key]) + for key, expected_gradient_value in expected_gradient.items(): + np.testing.assert_almost_equal(gradient[key], expected_gradient_value) def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): """Test for trotter qrte gradient with SummedOp and QDrift; missing param.""" @@ -268,9 +268,6 @@ def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): expected_gradient = {theta1: 0j} np.testing.assert_equal(gradient, expected_gradient) - # TODO unit tests for _try_binding_params, def _validate_hamiltonian_form(self, hamiltonian: - # SummedOp), _is_linear_with_single_param - if __name__ == "__main__": unittest.main() From 9a82a4a84bf6bf43e324245b8f80878db5fc0e17 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 1 Mar 2022 12:41:53 +0100 Subject: [PATCH 048/145] Code refactoring --- .../trotterization/test_trotter_ops_validator.py | 3 ++- .../real/implementations/trotterization/test_trotter_qrte.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py index a618c9f226b7..ffc2551e7e41 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py @@ -18,7 +18,8 @@ from ddt import ddt, data, unpack import numpy as np -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_ops_validator import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.\ + trotter_ops_validator import ( _validate_hamiltonian_form, _is_pauli_lin_single_param, ) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 2b5ac14468c2..ae20894e8daf 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -20,7 +20,8 @@ from numpy.testing import assert_raises from scipy.linalg import expm -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.\ + trotterization.trotter_qrte import ( TrotterQrte, ) from qiskit.quantum_info import Statevector From e40b0ca4a513b4fab9d31a87db8a4566239993bb Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 1 Mar 2022 14:49:13 +0100 Subject: [PATCH 049/145] Code review changes. --- .../time_evolution/evolution_gradient_base.py | 2 +- .../time_evolution/evolution_result.py | 16 +++++++++++++- .../problems/evolution_problem.py | 5 ++++- .../problems/test_evolution_problem.py | 20 ++++++++++++----- .../test_gradient_evolution_problem.py | 22 +++++++++++++------ .../time_evolution/test_evolution_result.py | 18 ++++++++++++++- 6 files changed, 66 insertions(+), 17 deletions(-) diff --git a/qiskit/algorithms/time_evolution/evolution_gradient_base.py b/qiskit/algorithms/time_evolution/evolution_gradient_base.py index ec6f6b94de90..ddee04db8f07 100644 --- a/qiskit/algorithms/time_evolution/evolution_gradient_base.py +++ b/qiskit/algorithms/time_evolution/evolution_gradient_base.py @@ -25,7 +25,7 @@ class GradientEvolutionBase(EvolutionBase, ABC): """Base class for quantum time evolution and gradient evolution.""" @abstractmethod - def gradient(self, gradient_evolution_problem: GradientEvolutionProblem) -> EvolutionResult: + def evolve_gradient(self, gradient_evolution_problem: GradientEvolutionProblem) -> EvolutionResult: """ Performs Quantum Time Evolution of gradient expressions. diff --git a/qiskit/algorithms/time_evolution/evolution_result.py b/qiskit/algorithms/time_evolution/evolution_result.py index ae82a549aae7..5d2110ac3ae3 100644 --- a/qiskit/algorithms/time_evolution/evolution_result.py +++ b/qiskit/algorithms/time_evolution/evolution_result.py @@ -17,7 +17,7 @@ class EvolutionResult(AlgorithmResult): - """Class for holding evolution result and relevant metadata.""" + """Class for holding evolution result.""" def __init__( self, @@ -29,6 +29,20 @@ def __init__( evolved_state: An evolved quantum state; mutually exclusive with evolved_observable. evolved_observable: An evolved quantum observable; mutually exclusive with evolved_state. + + Raises: + ValueError: If both or none evolved_state and evolved_observable are provided. """ + + if evolved_state is not None and evolved_observable is not None: + raise ValueError( + "evolved_state and evolved_observable are mutually exclusive; both provided." + ) + + if evolved_state is None and evolved_observable is None: + raise ValueError( + "One of evolved_state or evolved_observable must be provided; none provided." + ) + self.evolved_state = evolved_state self.evolved_observable = evolved_observable diff --git a/qiskit/algorithms/time_evolution/problems/evolution_problem.py b/qiskit/algorithms/time_evolution/problems/evolution_problem.py index c9a25e34c08c..777f231b3107 100644 --- a/qiskit/algorithms/time_evolution/problems/evolution_problem.py +++ b/qiskit/algorithms/time_evolution/problems/evolution_problem.py @@ -41,12 +41,15 @@ def __init__( values, including the t_param. Raises: - ValueError: if both initial_state and observable are provided. + ValueError: If both or none initial_state and observable are provided. """ if initial_state is not None and observable is not None: raise ValueError("initial_state and observable are mutually exclusive; both provided.") + if initial_state is None and observable is None: + raise ValueError("One of initial_state or observable must be provided; none provided.") + self.hamiltonian = hamiltonian self.time = time self.initial_state = initial_state diff --git a/test/python/algorithms/time_evolution/problems/test_evolution_problem.py b/test/python/algorithms/time_evolution/problems/test_evolution_problem.py index 2bd3f3fac2d6..28c197bd91c9 100644 --- a/test/python/algorithms/time_evolution/problems/test_evolution_problem.py +++ b/test/python/algorithms/time_evolution/problems/test_evolution_problem.py @@ -11,13 +11,16 @@ # that they have been altered from the originals. """Test evolution problem class.""" +import unittest from test.python.algorithms import QiskitAlgorithmsTestCase +from ddt import data, unpack, ddt from qiskit.algorithms.time_evolution.problems.evolution_problem import EvolutionProblem from qiskit.circuit import Parameter from qiskit.opflow import Y, Z, One +@ddt class TestEvolutionProblem(QiskitAlgorithmsTestCase): """Test evolution problem class.""" @@ -25,12 +28,13 @@ def test_init_default(self): """Tests that all default fields are initialized correctly.""" hamiltonian = Y time = 2.5 + initial_state = One - evo_problem = EvolutionProblem(hamiltonian, time) + evo_problem = EvolutionProblem(hamiltonian, time, initial_state) expected_hamiltonian = Y expected_time = 2.5 - expected_initial_state = None + expected_initial_state = One expected_observable = None expected_t_param = None expected_hamiltonian_value_dict = None @@ -69,16 +73,20 @@ def test_init_all(self): self.assertEqual(evo_problem.t_param, expected_t_param) self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) - def test_init_error(self): - """Tests that an error is raised when both initial_state and observable provided.""" + @data((One, Y), (None, None)) + @unpack + def test_init_error(self, initial_state, observable): + """Tests that an error is raised when both or none initial_state and observable provided.""" t_parameter = Parameter("t") hamiltonian = t_parameter * Z + Y time = 2 - initial_state = One - observable = Y hamiltonian_value_dict = {t_parameter: 3.2} with self.assertRaises(ValueError): _ = EvolutionProblem( hamiltonian, time, initial_state, observable, t_parameter, hamiltonian_value_dict ) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py b/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py index 2e00dcc78cc0..43dd6b382cc1 100644 --- a/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py +++ b/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py @@ -11,15 +11,18 @@ # that they have been altered from the originals. """Test gradient evolution problem class.""" +import unittest from test.python.algorithms import QiskitAlgorithmsTestCase +from ddt import ddt, data, unpack from qiskit.algorithms.time_evolution.problems.gradient_evolution_problem import ( GradientEvolutionProblem, ) from qiskit.circuit import Parameter -from qiskit.opflow import Y, Z, One, Gradient +from qiskit.opflow import Y, Z, One, Gradient, Zero +@ddt class TestGradientEvolutionProblem(QiskitAlgorithmsTestCase): """Test gradient evolution problem class.""" @@ -28,13 +31,14 @@ def test_init_default(self): hamiltonian = Y time = 2.5 gradient_object = Gradient() + initial_state = Zero - evo_problem = GradientEvolutionProblem(hamiltonian, time, gradient_object) + evo_problem = GradientEvolutionProblem(hamiltonian, time, gradient_object, initial_state) expected_hamiltonian = Y expected_time = 2.5 expected_gradient_object = gradient_object - expected_initial_state = None + expected_initial_state = Zero expected_observable = None expected_t_param = None expected_hamiltonian_value_dict = None @@ -91,16 +95,20 @@ def test_init_all(self): self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) self.assertEqual(evo_problem.gradient_params, expected_gradient_params) - def test_init_error(self): - """Tests that an error is raised when both initial_state and observable provided.""" + @data((One, Y), (None, None)) + @unpack + def test_init_error(self, initial_state, observable): + """Tests that an error is raised when both or none initial_state and observable provided.""" t_parameter = Parameter("t") hamiltonian = t_parameter * Z + Y time = 2 gradient_object = Gradient() - initial_state = One - observable = Y with self.assertRaises(ValueError): _ = GradientEvolutionProblem( hamiltonian, time, gradient_object, initial_state, observable ) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/python/algorithms/time_evolution/test_evolution_result.py b/test/python/algorithms/time_evolution/test_evolution_result.py index 2d8d40e3dd1d..b90590c76974 100644 --- a/test/python/algorithms/time_evolution/test_evolution_result.py +++ b/test/python/algorithms/time_evolution/test_evolution_result.py @@ -10,12 +10,16 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """Class for testing evolution result.""" +import unittest from test.python.algorithms import QiskitAlgorithmsTestCase +from ddt import unpack, ddt, data + from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult -from qiskit.opflow import Zero, X +from qiskit.opflow import Zero, X, One, Y +@ddt class TestEvolutionResult(QiskitAlgorithmsTestCase): """Class for testing evolution result and relevant metadata.""" @@ -40,3 +44,15 @@ def test_init_observable(self): self.assertEqual(evo_result.evolved_state, expected_state) self.assertEqual(evo_result.evolved_observable, expected_observable) + + @data((One, Y), (None, None)) + @unpack + def test_init_error(self, initial_state, observable): + """Tests that an error is raised when both or none initial_state and observable provided.""" + + with self.assertRaises(ValueError): + _ = EvolutionResult(initial_state, observable) + + +if __name__ == "__main__": + unittest.main() From ed8f26a863b3ebd920f429a50242fce221edfd79 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 1 Mar 2022 16:28:11 +0100 Subject: [PATCH 050/145] Removed gradient code for now. --- .../time_evolution/evolution_gradient_base.py | 40 ------ .../problems/gradient_evolution_problem.py | 55 --------- .../test_gradient_evolution_problem.py | 114 ------------------ 3 files changed, 209 deletions(-) delete mode 100644 qiskit/algorithms/time_evolution/evolution_gradient_base.py delete mode 100644 qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py delete mode 100644 test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py diff --git a/qiskit/algorithms/time_evolution/evolution_gradient_base.py b/qiskit/algorithms/time_evolution/evolution_gradient_base.py deleted file mode 100644 index ddee04db8f07..000000000000 --- a/qiskit/algorithms/time_evolution/evolution_gradient_base.py +++ /dev/null @@ -1,40 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 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. - -"""Base class for quantum time evolution and gradient evolution.""" - -from abc import ABC, abstractmethod - -from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult -from qiskit.algorithms.time_evolution.problems.gradient_evolution_problem import ( - GradientEvolutionProblem, -) -from qiskit.opflow import EvolutionBase - - -class GradientEvolutionBase(EvolutionBase, ABC): - """Base class for quantum time evolution and gradient evolution.""" - - @abstractmethod - def evolve_gradient(self, gradient_evolution_problem: GradientEvolutionProblem) -> EvolutionResult: - """ - Performs Quantum Time Evolution of gradient expressions. - - Args: - gradient_evolution_problem: GradientEvolutionProblem instance that includes definition - of a gradient evolution problem. - - Returns: - Evolution result which includes an evolved gradient of quantum state or an observable - and metadata. - """ - raise NotImplementedError() diff --git a/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py b/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py deleted file mode 100644 index 9ccb8ff226ee..000000000000 --- a/qiskit/algorithms/time_evolution/problems/gradient_evolution_problem.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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. - -"""Gradient evolution problem class.""" - -from typing import Union, List, Optional, Dict - -from qiskit.algorithms.time_evolution.problems.evolution_problem import EvolutionProblem -from qiskit.circuit import Parameter -from qiskit.opflow import OperatorBase, StateFn, Gradient - - -class GradientEvolutionProblem(EvolutionProblem): - """Gradient evolution problem class.""" - - def __init__( - self, - hamiltonian: OperatorBase, - time: float, - gradient_object: Gradient, - initial_state: Optional[StateFn] = None, - observable: Optional[OperatorBase] = None, - t_param: Optional[Parameter] = None, - hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, - gradient_params: Optional[List[Parameter]] = None, - ): - """ - Args: - hamiltonian: The Hamiltonian under which to evolve the system. - time: Total time of evolution. - gradient_object: Gradient object which defines a method for computing desired - gradients. - initial_state: Quantum state to be evolved. - observable: Observable to be evolved. - t_param: Time parameter in case of a time-dependent Hamiltonian. - hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values, including the t_param. - gradient_params: List of parameters that indicates with respect to which parameters - gradients shall be computed. - """ - super().__init__( - hamiltonian, time, initial_state, observable, t_param, hamiltonian_value_dict - ) - - self.gradient_object = gradient_object - self.gradient_params = gradient_params diff --git a/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py b/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py deleted file mode 100644 index 43dd6b382cc1..000000000000 --- a/test/python/algorithms/time_evolution/problems/test_gradient_evolution_problem.py +++ /dev/null @@ -1,114 +0,0 @@ -# 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. - -"""Test gradient evolution problem class.""" -import unittest - -from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import ddt, data, unpack -from qiskit.algorithms.time_evolution.problems.gradient_evolution_problem import ( - GradientEvolutionProblem, -) -from qiskit.circuit import Parameter -from qiskit.opflow import Y, Z, One, Gradient, Zero - - -@ddt -class TestGradientEvolutionProblem(QiskitAlgorithmsTestCase): - """Test gradient evolution problem class.""" - - def test_init_default(self): - """Tests that all default fields are initialized correctly.""" - hamiltonian = Y - time = 2.5 - gradient_object = Gradient() - initial_state = Zero - - evo_problem = GradientEvolutionProblem(hamiltonian, time, gradient_object, initial_state) - - expected_hamiltonian = Y - expected_time = 2.5 - expected_gradient_object = gradient_object - expected_initial_state = Zero - expected_observable = None - expected_t_param = None - expected_hamiltonian_value_dict = None - expected_gradient_params = None - - self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) - self.assertEqual(evo_problem.time, expected_time) - self.assertEqual(evo_problem.gradient_object, expected_gradient_object) - self.assertEqual(evo_problem.initial_state, expected_initial_state) - self.assertEqual(evo_problem.observable, expected_observable) - self.assertEqual(evo_problem.t_param, expected_t_param) - self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) - self.assertEqual(evo_problem.gradient_params, expected_gradient_params) - - def test_init_all(self): - """Tests that all fields are initialized correctly.""" - t_parameter = Parameter("t") - param = Parameter("x") - hamiltonian = t_parameter * Z + param * Y - time = 2 - gradient_object = Gradient() - - initial_state = One - observable = None - hamiltonian_value_dict = {t_parameter: 3.2} - gradient_params = [param] - - evo_problem = GradientEvolutionProblem( - hamiltonian, - time, - gradient_object, - initial_state, - observable, - t_parameter, - hamiltonian_value_dict, - gradient_params, - ) - - expected_hamiltonian = param * Y + t_parameter * Z - expected_time = 2 - expected_gradient_object = gradient_object - expected_initial_state = One - expected_observable = None - expected_t_param = t_parameter - expected_hamiltonian_value_dict = {t_parameter: 3.2} - expected_gradient_params = [param] - - self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) - self.assertEqual(evo_problem.time, expected_time) - self.assertEqual(evo_problem.gradient_object, expected_gradient_object) - self.assertEqual(evo_problem.initial_state, expected_initial_state) - self.assertEqual(evo_problem.observable, expected_observable) - self.assertEqual(evo_problem.t_param, expected_t_param) - self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) - self.assertEqual(evo_problem.gradient_params, expected_gradient_params) - - @data((One, Y), (None, None)) - @unpack - def test_init_error(self, initial_state, observable): - """Tests that an error is raised when both or none initial_state and observable provided.""" - t_parameter = Parameter("t") - hamiltonian = t_parameter * Z + Y - time = 2 - gradient_object = Gradient() - - with self.assertRaises(ValueError): - _ = GradientEvolutionProblem( - hamiltonian, time, gradient_object, initial_state, observable - ) - - -if __name__ == "__main__": - unittest.main() From 069cbcb0255e21ea793d183408398da4913f53be Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 1 Mar 2022 16:33:07 +0100 Subject: [PATCH 051/145] Code review refactoring. --- .../implementations/trotterization/trotter_ops_validator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py index 6b76418df6fe..1a8eb00b21e7 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py @@ -110,7 +110,7 @@ def _is_pauli_lin_single_param(operator: PauliOp) -> bool: """ if not isinstance(operator, PauliOp): raise ValueError(f"Only PauliOp expected. {type(operator)} provided") - if _is_operator_no_parameters(operator): + if _is_operator_parametrized(operator): return True if len(operator.coeff.parameters) > 1: raise ValueError( @@ -139,7 +139,7 @@ def _operator_derivative( return gradient -def _is_operator_no_parameters(operator: PauliOp) -> bool: +def _is_operator_parametrized(operator: PauliOp) -> bool: """ Checks if an operator is parametrized. From 21bbf715a0660bcc74575df1cdd0900baede97a3 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 1 Mar 2022 16:37:05 +0100 Subject: [PATCH 052/145] Removed gradient code for now. --- .../quantum_time_evolution/evolution_base.py | 14 -- .../trotterization/trotter_qrte.py | 222 +----------------- .../quantum_time_evolution/real/qrte.py | 14 -- .../trotterization/test_trotter_qrte.py | 126 ---------- 4 files changed, 3 insertions(+), 373 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/evolution_base.py b/qiskit/algorithms/quantum_time_evolution/evolution_base.py index b2f5044f3d99..495c318d835d 100644 --- a/qiskit/algorithms/quantum_time_evolution/evolution_base.py +++ b/qiskit/algorithms/quantum_time_evolution/evolution_base.py @@ -48,17 +48,3 @@ def evolve( """ raise NotImplementedError() - @abstractmethod - def gradient( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn, - gradient_object: Gradient, - observable: OperatorBase = None, - t_param: Parameter = None, - hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, - gradient_params: List[Parameter] = None, - ): - """Performs Quantum Time Evolution of gradient expressions.""" - raise NotImplementedError() diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py index ae4edd8af694..9b39500872ff 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py @@ -12,12 +12,10 @@ """An algorithm to implement a Trotterization real time-evolution.""" -from collections import defaultdict -from itertools import product -from typing import Union, List, Dict, Optional +from typing import Union, Dict -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_ops_validator import ( - _validate_hamiltonian_form, +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization\ + .trotter_ops_validator import ( _validate_input, _is_op_bound, ) @@ -26,8 +24,6 @@ from qiskit.opflow import ( OperatorBase, StateFn, - Gradient, - commutator, SummedOp, PauliSumOp, PauliOp, @@ -174,215 +170,3 @@ def _try_binding_params( f"SummedOp, PauliOp, and OperatorBase base are supported by TrotterQrte." ) - def gradient( - self, - hamiltonian: Union[SummedOp, PauliOp, OperatorBase], - time: float, - initial_state: StateFn, - gradient_object: Gradient, - observable: OperatorBase = None, - t_param: Parameter = None, - hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, - gradient_params: List[Parameter] = None, - ) -> Dict[Parameter, Union[float, complex]]: - """ - Computes gradients of a quantum state evolved according to a given Hamiltonian and w.r.t. - observable and parameters provided. - - Args: - hamiltonian: The operator to evolve. Can also be provided as list of non-commuting - operators where the elements are sums of commuting operators. - For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. - time: Total time of evolution. - initial_state: If interested in a quantum state time evolution, a quantum state to be - evolved. - gradient_object: TrotterQrte does not support custom Gradient method. Provided Gradient - object is ignored. - observable: If interested in a quantum observable time evolution, a quantum observable - to be evolved. - t_param: Not supported by this algorithm. - hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values. - gradient_params: Parameters w.r.t. which gradients shall be computed. - - Returns: - The dictionary of parameters and respective evolved gradients. - - Raises: - ValueError: If a Hamiltonian is not of an expected form and/or type or a gradient w.r.t. - time is requested. - NotImplementedError: If no observable is provided. - Warning: If a gradient_object is provided. - """ - if observable is None: - raise NotImplementedError( - "Observable not provided. Probability gradients are not yet supported by " - "TrotterQrte. " - ) - if gradient_object is not None: - raise Warning( - "TrotterQrte does not support custom Gradient method. Provided Gradient object is " - "ignored." - ) - if t_param is not None: - raise ValueError( - "TrotterQrte does not accept a time dependent hamiltonian," - "t_param should be set to None." - ) - - _validate_hamiltonian_form(hamiltonian) - param_set = set(gradient_params) - - # when t_param becomes supported - # if t_param is not None: - # param_set.add(t_param) - - if isinstance(hamiltonian, PauliOp): - hamiltonian = SummedOp([hamiltonian]) - - if param_set.issubset(set(hamiltonian.parameters)): - gradients = defaultdict(float) - if isinstance(hamiltonian, SummedOp): - # access through gradient_params rather param_set for testing purposes. set() adds - # the elements in random order and otherwise there is no way to seed the results. - for gradient_param, hamiltonian_term in product( - gradient_params, hamiltonian.oplist - ): - if gradient_param in hamiltonian_term.parameters: - self._calc_term_gradient_by_type( - gradient_param, - gradients, - hamiltonian, - hamiltonian_term, - hamiltonian_value_dict, - initial_state, - observable, - t_param, - time, - ) - - return gradients - - else: - raise ValueError( - "gradient_params provided that are not found in hamiltonian.params " - "and not a t_param." - ) - - def _calc_term_gradient_by_type( - self, - gradient_param, - gradients, - hamiltonian, - hamiltonian_term, - hamiltonian_value_dict, - initial_state, - observable, - t_param, - time, - ): - if gradient_param == t_param: - finite_difference = self._calc_time_gradient_finite_diff( - hamiltonian, - hamiltonian_value_dict, - initial_state, - observable, - t_param, - time, - ) - gradients[gradient_param] += finite_difference.eval() - else: - gradient = self._calc_term_gradient( - hamiltonian, - hamiltonian_term, - initial_state, - observable, - t_param, - time, - hamiltonian_value_dict, - ) - gradients[gradient_param] += gradient - - def _calc_time_gradient_finite_diff( - self, - hamiltonian: Union[SummedOp, PauliOp, OperatorBase], - hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], - initial_state: StateFn, - observable: OperatorBase, - t_param: Parameter, - time: float, - epsilon: float = 0.01, - ) -> float: - """Calculates a gradient using the finite difference method. It is used for gradients - w.r.t. a potential time parameter. - - Args: - hamiltonian: The operator to evolve. Can also be provided as list of non-commuting - operators where the elements are sums of commuting operators. - For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. - hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values. - initial_state: If interested in a quantum state time evolution, a quantum state to be - evolved. - observable: If interested in a quantum observable time evolution, a quantum observable - to be evolved. - t_param: Not supported by this algorithm. - time: Total time of evolution. - epsilon: Size of a finite difference interval. - - Returns: - Finite difference gradient w.r.t. time parameter of an evolved state. - """ - hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) - evolved_state1 = self.evolve(hamiltonian, time + epsilon, initial_state, t_param=t_param) - evolved_state2 = self.evolve(hamiltonian, time - epsilon, initial_state, t_param=t_param) - expected_val_1 = ~StateFn(observable) @ evolved_state1 - expected_val_2 = ~StateFn(observable) @ evolved_state2 - finite_difference = (expected_val_1 - expected_val_2) / (2 * epsilon) - return finite_difference - - def _calc_term_gradient( - self, - hamiltonian: Union[SummedOp, PauliOp, OperatorBase], - hamiltonian_term: Union[PauliOp, OperatorBase], - initial_state: StateFn, - observable: OperatorBase, - t_param: Optional[Parameter], - time: float, - hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], - ) -> float: - """Calculates a gradient of a Hamiltonian term (a single summand) with respect to - parameters given. - - Args: - hamiltonian: The operator to evolve. Can also be provided as list of non-commuting - operators where the elements are sums of commuting operators. - For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. - hamiltonian_term: One of the terms (summands) from a Hamiltonian. - initial_state: If interested in a quantum state time evolution, a quantum state to be - evolved. - observable: If interested in a quantum observable time evolution, a quantum observable - to be evolved. - t_param: Not supported by this algorithm. - time: Total time of evolution. - hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values. - - Returns: - Gradient of an evolved state w.r.t. parameters from a hamiltonian_value_dict. - """ - hamiltonian_term = self._try_binding_params(hamiltonian_term, hamiltonian_value_dict) - custom_observable = commutator(1j * time * hamiltonian_term, observable) - hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) - - evolved_state = self.evolve( - hamiltonian, - time, - initial_state, - t_param=t_param, - hamiltonian_value_dict=hamiltonian_value_dict, - ) - - gradient = ~StateFn(custom_observable) @ evolved_state - gradient = gradient.eval() - return gradient diff --git a/qiskit/algorithms/quantum_time_evolution/real/qrte.py b/qiskit/algorithms/quantum_time_evolution/real/qrte.py index 63cdbd12876c..2c3acb528d55 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/qrte.py +++ b/qiskit/algorithms/quantum_time_evolution/real/qrte.py @@ -37,17 +37,3 @@ def evolve( """ raise NotImplementedError() - @abstractmethod - def gradient( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn, - gradient_object: Gradient, - observable: OperatorBase = None, - t_param=None, - hamiltonian_value_dict=None, - gradient_params=None, - ): - """Performs Quantum Real Time Evolution of gradient expressions.""" - raise NotImplementedError() diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index ae20894e8daf..2a287a74b233 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -143,132 +143,6 @@ def test_trotter_qrte_trotter_binding_missing_param(self): with assert_raises(ValueError): _ = trotter_qrte.evolve(operator, 1, initial_state) - def test_trotter_qrte_gradient_summed_op_qdrift(self): - """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" - theta1 = Parameter("theta1") - theta2 = Parameter("theta2") - operator = theta1 * (I ^ Z ^ I) + theta2 * (Z ^ I ^ I) - trotter_qrte = TrotterQrte(QDrift()) - initial_state = Zero - time = 5 - gradient_object = None - observable = Z ^ Y ^ Z - value_dict = {theta1: 2, theta2: 3} - gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - hamiltonian_value_dict=value_dict, - gradient_params=[theta1, theta2], - ) - expected_gradient = {theta1: 0j, theta2: 0j} - np.testing.assert_equal(gradient, expected_gradient) - - def test_trotter_qrte_gradient_summed_op_qdrift_2(self): - """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" - algorithm_globals.random_seed = 0 - theta1 = Parameter("theta1") - theta2 = Parameter("theta2") - operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) - trotter_qrte = TrotterQrte(QDrift()) - initial_state = Zero - time = 5 - gradient_object = None - observable = X ^ Y ^ Z - value_dict = {theta1: 2, theta2: 3} - gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - hamiltonian_value_dict=value_dict, - gradient_params=[theta1, theta2], - ) - expected_gradient = {theta1: 0j, theta2: 0j} - np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) - for key, expected_gradient_value in expected_gradient.items(): - np.testing.assert_almost_equal(gradient[key], expected_gradient_value) - - def test_trotter_qrte_gradient_summed_op_qdrift_3(self): - """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators.""" - algorithm_globals.random_seed = 0 - theta1 = Parameter("theta1") - theta2 = Parameter("theta2") - operator = (theta1 * (Y ^ Z ^ Y)) + theta2 * (Z ^ X ^ X) - trotter_qrte = TrotterQrte(QDrift()) - initial_state = Zero - time = 5 - gradient_object = None - observable = -1j * (X ^ Y ^ Z) - value_dict = {theta1: 2, theta2: 3} - gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - hamiltonian_value_dict=value_dict, - gradient_params=[theta1, theta2], - ) - expected_gradient = {theta1: 0j, theta2: 0j} - np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) - for key, expected_gradient_value in expected_gradient.items(): - np.testing.assert_almost_equal(gradient[key], expected_gradient_value) - - # TODO fix problem with randomness causing different results - def test_trotter_qrte_gradient_summed_op_qdrift_4(self): - """Test for trotter qrte gradient with SummedOp and QDrift with commuting operators - with complex parameter binding.""" - algorithm_globals.random_seed = 0 - theta1 = Parameter("theta1") - theta2 = Parameter("theta2") - operator = theta1 * (Z) + theta2 * (Y) - trotter_qrte = TrotterQrte(QDrift()) - initial_state = Zero - time = 5 - gradient_object = None - observable = X - value_dict = {theta1: 2, theta2: 3} - gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - hamiltonian_value_dict=value_dict, - gradient_params=[theta1, theta2], - ) - expected_gradient = {theta1: 10.938002663771012 + 0j, theta2: 9.745099298494452 + 0j} - np.testing.assert_equal(expected_gradient.keys(), gradient.keys()) - for key, expected_gradient_value in expected_gradient.items(): - np.testing.assert_almost_equal(gradient[key], expected_gradient_value) - - def test_trotter_qrte_gradient_summed_op_qdrift_no_param(self): - """Test for trotter qrte gradient with SummedOp and QDrift; missing param.""" - algorithm_globals.random_seed = 0 - theta1 = Parameter("theta1") - operator = theta1 * (I ^ Z ^ I) + 5 * (Z ^ I ^ I) - trotter_qrte = TrotterQrte(QDrift()) - initial_state = Zero - time = 5 - gradient_object = None - observable = Z - value_dict = {theta1: 2} - gradient = trotter_qrte.gradient( - operator, - time, - initial_state, - gradient_object, - observable, - hamiltonian_value_dict=value_dict, - gradient_params=[theta1], - ) - expected_gradient = {theta1: 0j} - np.testing.assert_equal(gradient, expected_gradient) - if __name__ == "__main__": unittest.main() From cba5911f626c32dd0ad0d5ca21bd627cb44ed452 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 1 Mar 2022 16:59:17 +0100 Subject: [PATCH 053/145] Evolving observable removed. Evaluating observables added. --- .../time_evolution/evolution_base.py | 5 ++- .../time_evolution/evolution_result.py | 34 +++++++------------ .../problems/evolution_problem.py | 24 +++++-------- .../problems/test_evolution_problem.py | 30 ++++------------ .../time_evolution/test_evolution_result.py | 28 +++++---------- 5 files changed, 39 insertions(+), 82 deletions(-) diff --git a/qiskit/algorithms/time_evolution/evolution_base.py b/qiskit/algorithms/time_evolution/evolution_base.py index aa698376db0c..eebfc780def6 100644 --- a/qiskit/algorithms/time_evolution/evolution_base.py +++ b/qiskit/algorithms/time_evolution/evolution_base.py @@ -24,14 +24,13 @@ class EvolutionBase(ABC): @abstractmethod def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: """ - Evolves an initial state or an observable according to a Hamiltonian provided. + Evolves an initial state in the evolution_problem according to a Hamiltonian provided. Args: evolution_problem: EvolutionProblem instance that includes definition of an evolution problem. Returns: - Evolution result which includes an evolved gradient of quantum state or an observable - and metadata. + Evolution result which includes an evolved quantum state. """ raise NotImplementedError() diff --git a/qiskit/algorithms/time_evolution/evolution_result.py b/qiskit/algorithms/time_evolution/evolution_result.py index 5d2110ac3ae3..986187677a9e 100644 --- a/qiskit/algorithms/time_evolution/evolution_result.py +++ b/qiskit/algorithms/time_evolution/evolution_result.py @@ -9,11 +9,13 @@ # 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. -"""Class for holding evolution result and relevant metadata.""" -from typing import Optional +"""Class for holding evolution result.""" +from typing import Optional, Union, Tuple +from qiskit import QuantumCircuit from qiskit.algorithms import AlgorithmResult -from qiskit.opflow import OperatorBase +from qiskit.algorithms.eigen_solvers.eigen_solver import ListOrDict +from qiskit.opflow import StateFn class EvolutionResult(AlgorithmResult): @@ -21,28 +23,16 @@ class EvolutionResult(AlgorithmResult): def __init__( self, - evolved_state: Optional[OperatorBase] = None, - evolved_observable: Optional[OperatorBase] = None, + evolved_state: Union[StateFn, QuantumCircuit], + aux_ops_evaluated: Optional[ListOrDict[Tuple[complex, complex]]] = None ): """ Args: - evolved_state: An evolved quantum state; mutually exclusive with evolved_observable. - evolved_observable: An evolved quantum observable; mutually exclusive with - evolved_state. - - Raises: - ValueError: If both or none evolved_state and evolved_observable are provided. + evolved_state: An evolved quantum state. + aux_ops_evaluated: Optional list of observables for which expected values on an evolved + state are calculated. These values are in fact tuples formatted as (mean, standard + deviation). """ - if evolved_state is not None and evolved_observable is not None: - raise ValueError( - "evolved_state and evolved_observable are mutually exclusive; both provided." - ) - - if evolved_state is None and evolved_observable is None: - raise ValueError( - "One of evolved_state or evolved_observable must be provided; none provided." - ) - self.evolved_state = evolved_state - self.evolved_observable = evolved_observable + self.aux_ops_evaluated = aux_ops_evaluated diff --git a/qiskit/algorithms/time_evolution/problems/evolution_problem.py b/qiskit/algorithms/time_evolution/problems/evolution_problem.py index 777f231b3107..a13c62f2fcea 100644 --- a/qiskit/algorithms/time_evolution/problems/evolution_problem.py +++ b/qiskit/algorithms/time_evolution/problems/evolution_problem.py @@ -14,6 +14,8 @@ from typing import Union, Optional, Dict +from qiskit import QuantumCircuit +from qiskit.algorithms.eigen_solvers.eigen_solver import ListOrDict from qiskit.circuit import Parameter from qiskit.opflow import OperatorBase, StateFn @@ -25,34 +27,26 @@ def __init__( self, hamiltonian: OperatorBase, time: float, - initial_state: Optional[StateFn] = None, - observable: Optional[OperatorBase] = None, + initial_state: Union[StateFn, QuantumCircuit], + aux_operators: Optional[ListOrDict[OperatorBase]] = None, t_param: Optional[Parameter] = None, - hamiltonian_value_dict: Optional[Dict[Parameter, Union[float, complex]]] = None, + hamiltonian_value_dict: Optional[Dict[Parameter, Union[complex]]] = None, ): """ Args: hamiltonian: The Hamiltonian under which to evolve the system. time: Total time of evolution. - initial_state: Quantum state to be evolved; mutually exclusive with observable. - observable: Observable to be evolved; mutually exclusive with initial_state. + initial_state: Quantum state to be evolved. + aux_operators: Optional list of auxiliary operators to be evaluated with the + evolved initial_state and their expectation values returned. t_param: Time parameter in case of a time-dependent Hamiltonian. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to certain values, including the t_param. - - Raises: - ValueError: If both or none initial_state and observable are provided. """ - if initial_state is not None and observable is not None: - raise ValueError("initial_state and observable are mutually exclusive; both provided.") - - if initial_state is None and observable is None: - raise ValueError("One of initial_state or observable must be provided; none provided.") - self.hamiltonian = hamiltonian self.time = time self.initial_state = initial_state - self.observable = observable + self.aux_operators = aux_operators self.t_param = t_param self.hamiltonian_value_dict = hamiltonian_value_dict diff --git a/test/python/algorithms/time_evolution/problems/test_evolution_problem.py b/test/python/algorithms/time_evolution/problems/test_evolution_problem.py index 28c197bd91c9..30f42f4eb998 100644 --- a/test/python/algorithms/time_evolution/problems/test_evolution_problem.py +++ b/test/python/algorithms/time_evolution/problems/test_evolution_problem.py @@ -14,13 +14,11 @@ import unittest from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import data, unpack, ddt from qiskit.algorithms.time_evolution.problems.evolution_problem import EvolutionProblem from qiskit.circuit import Parameter -from qiskit.opflow import Y, Z, One +from qiskit.opflow import Y, Z, One, X -@ddt class TestEvolutionProblem(QiskitAlgorithmsTestCase): """Test evolution problem class.""" @@ -35,14 +33,14 @@ def test_init_default(self): expected_hamiltonian = Y expected_time = 2.5 expected_initial_state = One - expected_observable = None + expected_aux_operators = None expected_t_param = None expected_hamiltonian_value_dict = None self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) self.assertEqual(evo_problem.time, expected_time) self.assertEqual(evo_problem.initial_state, expected_initial_state) - self.assertEqual(evo_problem.observable, expected_observable) + self.assertEqual(evo_problem.aux_operators, expected_aux_operators) self.assertEqual(evo_problem.t_param, expected_t_param) self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) @@ -52,41 +50,27 @@ def test_init_all(self): hamiltonian = t_parameter * Z + Y time = 2 initial_state = One - observable = None + aux_operators = [X, Y] hamiltonian_value_dict = {t_parameter: 3.2} evo_problem = EvolutionProblem( - hamiltonian, time, initial_state, observable, t_parameter, hamiltonian_value_dict + hamiltonian, time, initial_state, aux_operators, t_parameter, hamiltonian_value_dict ) expected_hamiltonian = Y + t_parameter * Z expected_time = 2 expected_initial_state = One - expected_observable = None + expected_aux_operators = [X, Y] expected_t_param = t_parameter expected_hamiltonian_value_dict = {t_parameter: 3.2} self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) self.assertEqual(evo_problem.time, expected_time) self.assertEqual(evo_problem.initial_state, expected_initial_state) - self.assertEqual(evo_problem.observable, expected_observable) + self.assertEqual(evo_problem.aux_operators, expected_aux_operators) self.assertEqual(evo_problem.t_param, expected_t_param) self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) - @data((One, Y), (None, None)) - @unpack - def test_init_error(self, initial_state, observable): - """Tests that an error is raised when both or none initial_state and observable provided.""" - t_parameter = Parameter("t") - hamiltonian = t_parameter * Z + Y - time = 2 - hamiltonian_value_dict = {t_parameter: 3.2} - - with self.assertRaises(ValueError): - _ = EvolutionProblem( - hamiltonian, time, initial_state, observable, t_parameter, hamiltonian_value_dict - ) - if __name__ == "__main__": unittest.main() diff --git a/test/python/algorithms/time_evolution/test_evolution_result.py b/test/python/algorithms/time_evolution/test_evolution_result.py index b90590c76974..208cfb0e028a 100644 --- a/test/python/algorithms/time_evolution/test_evolution_result.py +++ b/test/python/algorithms/time_evolution/test_evolution_result.py @@ -13,13 +13,10 @@ import unittest from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import unpack, ddt, data - from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult -from qiskit.opflow import Zero, X, One, Y +from qiskit.opflow import Zero -@ddt class TestEvolutionResult(QiskitAlgorithmsTestCase): """Class for testing evolution result and relevant metadata.""" @@ -29,29 +26,22 @@ def test_init_state(self): evo_result = EvolutionResult(evolved_state=evolved_state) expected_state = Zero - expected_observable = None + expected_aux_ops_evaluated = None self.assertEqual(evo_result.evolved_state, expected_state) - self.assertEqual(evo_result.evolved_observable, expected_observable) + self.assertEqual(evo_result.aux_ops_evaluated, expected_aux_ops_evaluated) def test_init_observable(self): """Tests that a class is initialized correctly with an evolved_observable.""" - evolved_observable = X - evo_result = EvolutionResult(evolved_observable=evolved_observable) + evolved_state = Zero + evolved_aux_ops_evaluated = [(5j, 5j), (1.0, 8j), (5 + 1j, 6 + 1j)] + evo_result = EvolutionResult(evolved_state, evolved_aux_ops_evaluated) - expected_state = None - expected_observable = X + expected_state = Zero + expected_aux_ops_evaluated = [(5j, 5j), (1.0, 8j), (5 + 1j, 6 + 1j)] self.assertEqual(evo_result.evolved_state, expected_state) - self.assertEqual(evo_result.evolved_observable, expected_observable) - - @data((One, Y), (None, None)) - @unpack - def test_init_error(self, initial_state, observable): - """Tests that an error is raised when both or none initial_state and observable provided.""" - - with self.assertRaises(ValueError): - _ = EvolutionResult(initial_state, observable) + self.assertEqual(evo_result.aux_ops_evaluated, expected_aux_ops_evaluated) if __name__ == "__main__": From 0a88dc49b62b1c4b07a79bc16929a34a122a269a Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 3 Mar 2022 11:15:08 +0100 Subject: [PATCH 054/145] Code refactoring. --- .../trotterization/trotter_ops_validator.py | 24 ------------------- .../trotterization/test_trotter_qrte.py | 3 +-- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py index 1a8eb00b21e7..4cc82524d024 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py +++ b/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py @@ -18,7 +18,6 @@ from qiskit.circuit import Parameter, ParameterExpression from qiskit.opflow import ( OperatorBase, - StateFn, SummedOp, PauliOp, ) @@ -40,29 +39,6 @@ def _is_op_bound(operator: Union[SummedOp, PauliOp, OperatorBase]) -> None: ) -def _validate_input(initial_state: StateFn, observable: OperatorBase) -> None: - """ - Validates if one and only one among initial_state and observable is provided. - - Args: - initial_state: A variable potentially holding a quantum state. - observable: A variable potentially holding a quantum observable. - - Raises: - ValueError: If both initial_state and observable are provided or none of them. - """ - if initial_state is None and observable is None: - raise ValueError( - "TrotterQrte requires an initial state or an observable to be evolved; None " - "provided." - ) - if initial_state is not None and observable is not None: - raise ValueError( - "TrotterQrte requires an initial state or an observable to be evolved; both " - "provided." - ) - - def _validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBase]): """Validates that a Hamiltonian is of a correct type and with expected dependence on parameters. diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 2a287a74b233..928eaf3fa2ca 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -20,8 +20,7 @@ from numpy.testing import assert_raises from scipy.linalg import expm -from qiskit.algorithms.quantum_time_evolution.real.implementations.\ - trotterization.trotter_qrte import ( +from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( TrotterQrte, ) from qiskit.quantum_info import Statevector From 94574b8d4a78355f2882828fada3c570452b8a86 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 3 Mar 2022 13:11:21 +0100 Subject: [PATCH 055/145] Improved naming. --- .../time_evolution/{evolution_base.py => evolver.py} | 12 ++++++------ .../{evolution_result.py => evolver_result.py} | 4 ++-- .../{evolution_problem.py => evolver_problem.py} | 6 +++--- ..._evolution_problem.py => test_evolver_problem.py} | 12 ++++++------ ...st_evolution_result.py => test_evolver_result.py} | 8 ++++---- 5 files changed, 21 insertions(+), 21 deletions(-) rename qiskit/algorithms/time_evolution/{evolution_base.py => evolver.py} (65%) rename qiskit/algorithms/time_evolution/{evolution_result.py => evolver_result.py} (96%) rename qiskit/algorithms/time_evolution/problems/{evolution_problem.py => evolver_problem.py} (95%) rename test/python/algorithms/time_evolution/problems/{test_evolution_problem.py => test_evolver_problem.py} (88%) rename test/python/algorithms/time_evolution/{test_evolution_result.py => test_evolver_result.py} (85%) diff --git a/qiskit/algorithms/time_evolution/evolution_base.py b/qiskit/algorithms/time_evolution/evolver.py similarity index 65% rename from qiskit/algorithms/time_evolution/evolution_base.py rename to qiskit/algorithms/time_evolution/evolver.py index eebfc780def6..a7816e1b0d2b 100644 --- a/qiskit/algorithms/time_evolution/evolution_base.py +++ b/qiskit/algorithms/time_evolution/evolver.py @@ -14,23 +14,23 @@ from abc import ABC, abstractmethod -from qiskit.algorithms.time_evolution.problems.evolution_problem import EvolutionProblem -from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult +from qiskit.algorithms.time_evolution.problems.evolver_problem import EvolverProblem +from qiskit.algorithms.time_evolution.evolver_result import EvolverResult -class EvolutionBase(ABC): +class Evolver(ABC): """Base class for quantum time evolution.""" @abstractmethod - def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: + def evolve(self, evolver_problem: EvolverProblem) -> EvolverResult: """ Evolves an initial state in the evolution_problem according to a Hamiltonian provided. Args: - evolution_problem: EvolutionProblem instance that includes definition of an evolution + evolver_problem: EvolverProblem instance that includes definition of an evolution problem. Returns: - Evolution result which includes an evolved quantum state. + Evolver result which includes an evolved quantum state. """ raise NotImplementedError() diff --git a/qiskit/algorithms/time_evolution/evolution_result.py b/qiskit/algorithms/time_evolution/evolver_result.py similarity index 96% rename from qiskit/algorithms/time_evolution/evolution_result.py rename to qiskit/algorithms/time_evolution/evolver_result.py index 986187677a9e..214e4b29d914 100644 --- a/qiskit/algorithms/time_evolution/evolution_result.py +++ b/qiskit/algorithms/time_evolution/evolver_result.py @@ -18,13 +18,13 @@ from qiskit.opflow import StateFn -class EvolutionResult(AlgorithmResult): +class EvolverResult(AlgorithmResult): """Class for holding evolution result.""" def __init__( self, evolved_state: Union[StateFn, QuantumCircuit], - aux_ops_evaluated: Optional[ListOrDict[Tuple[complex, complex]]] = None + aux_ops_evaluated: Optional[ListOrDict[Tuple[complex, complex]]] = None, ): """ Args: diff --git a/qiskit/algorithms/time_evolution/problems/evolution_problem.py b/qiskit/algorithms/time_evolution/problems/evolver_problem.py similarity index 95% rename from qiskit/algorithms/time_evolution/problems/evolution_problem.py rename to qiskit/algorithms/time_evolution/problems/evolver_problem.py index a13c62f2fcea..8baf17a766fe 100644 --- a/qiskit/algorithms/time_evolution/problems/evolution_problem.py +++ b/qiskit/algorithms/time_evolution/problems/evolver_problem.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Evolution problem class.""" +"""Evolver problem class.""" from typing import Union, Optional, Dict @@ -20,8 +20,8 @@ from qiskit.opflow import OperatorBase, StateFn -class EvolutionProblem: - """Evolution problem class.""" +class EvolverProblem: + """Evolver problem class.""" def __init__( self, diff --git a/test/python/algorithms/time_evolution/problems/test_evolution_problem.py b/test/python/algorithms/time_evolution/problems/test_evolver_problem.py similarity index 88% rename from test/python/algorithms/time_evolution/problems/test_evolution_problem.py rename to test/python/algorithms/time_evolution/problems/test_evolver_problem.py index 30f42f4eb998..069684ef7909 100644 --- a/test/python/algorithms/time_evolution/problems/test_evolution_problem.py +++ b/test/python/algorithms/time_evolution/problems/test_evolver_problem.py @@ -10,17 +10,17 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Test evolution problem class.""" +"""Test evolver problem class.""" import unittest from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit.algorithms.time_evolution.problems.evolution_problem import EvolutionProblem +from qiskit.algorithms.time_evolution.problems.evolver_problem import EvolverProblem from qiskit.circuit import Parameter from qiskit.opflow import Y, Z, One, X -class TestEvolutionProblem(QiskitAlgorithmsTestCase): - """Test evolution problem class.""" +class TestEvolverProblem(QiskitAlgorithmsTestCase): + """Test evolver problem class.""" def test_init_default(self): """Tests that all default fields are initialized correctly.""" @@ -28,7 +28,7 @@ def test_init_default(self): time = 2.5 initial_state = One - evo_problem = EvolutionProblem(hamiltonian, time, initial_state) + evo_problem = EvolverProblem(hamiltonian, time, initial_state) expected_hamiltonian = Y expected_time = 2.5 @@ -53,7 +53,7 @@ def test_init_all(self): aux_operators = [X, Y] hamiltonian_value_dict = {t_parameter: 3.2} - evo_problem = EvolutionProblem( + evo_problem = EvolverProblem( hamiltonian, time, initial_state, aux_operators, t_parameter, hamiltonian_value_dict ) diff --git a/test/python/algorithms/time_evolution/test_evolution_result.py b/test/python/algorithms/time_evolution/test_evolver_result.py similarity index 85% rename from test/python/algorithms/time_evolution/test_evolution_result.py rename to test/python/algorithms/time_evolution/test_evolver_result.py index 208cfb0e028a..332998a4135a 100644 --- a/test/python/algorithms/time_evolution/test_evolution_result.py +++ b/test/python/algorithms/time_evolution/test_evolver_result.py @@ -13,17 +13,17 @@ import unittest from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit.algorithms.time_evolution.evolution_result import EvolutionResult +from qiskit.algorithms.time_evolution.evolver_result import EvolverResult from qiskit.opflow import Zero -class TestEvolutionResult(QiskitAlgorithmsTestCase): +class TestEvolverResult(QiskitAlgorithmsTestCase): """Class for testing evolution result and relevant metadata.""" def test_init_state(self): """Tests that a class is initialized correctly with an evolved_state.""" evolved_state = Zero - evo_result = EvolutionResult(evolved_state=evolved_state) + evo_result = EvolverResult(evolved_state=evolved_state) expected_state = Zero expected_aux_ops_evaluated = None @@ -35,7 +35,7 @@ def test_init_observable(self): """Tests that a class is initialized correctly with an evolved_observable.""" evolved_state = Zero evolved_aux_ops_evaluated = [(5j, 5j), (1.0, 8j), (5 + 1j, 6 + 1j)] - evo_result = EvolutionResult(evolved_state, evolved_aux_ops_evaluated) + evo_result = EvolverResult(evolved_state, evolved_aux_ops_evaluated) expected_state = Zero expected_aux_ops_evaluated = [(5j, 5j), (1.0, 8j), (5 + 1j, 6 + 1j)] From 6b93f03a7dab3933ac8bb824a4f8a4142d9330e2 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 3 Mar 2022 13:32:17 +0100 Subject: [PATCH 056/145] Improved folder structure; filled evolvers init file. --- .../{time_evolution => evolvers}/__init__.py | 16 ++++++++++++++++ .../{time_evolution => evolvers}/evolver.py | 8 ++++---- .../problems => evolvers}/evolver_problem.py | 0 .../evolver_result.py | 0 .../imaginary/__init__.py | 0 .../imaginary/qite.py | 4 ++-- .../real/__init__.py | 0 .../{time_evolution => evolvers}/real/qrte.py | 4 ++-- .../time_evolution/problems/__init__.py | 11 ----------- .../{time_evolution => evolvers}/__init__.py | 0 .../problems/__init__.py | 0 .../problems/test_evolver_problem.py | 2 +- .../test_evolver_result.py | 2 +- 13 files changed, 26 insertions(+), 21 deletions(-) rename qiskit/algorithms/{time_evolution => evolvers}/__init__.py (56%) rename qiskit/algorithms/{time_evolution => evolvers}/evolver.py (80%) rename qiskit/algorithms/{time_evolution/problems => evolvers}/evolver_problem.py (100%) rename qiskit/algorithms/{time_evolution => evolvers}/evolver_result.py (100%) rename qiskit/algorithms/{time_evolution => evolvers}/imaginary/__init__.py (100%) rename qiskit/algorithms/{time_evolution => evolvers}/imaginary/qite.py (76%) rename qiskit/algorithms/{time_evolution => evolvers}/real/__init__.py (100%) rename qiskit/algorithms/{time_evolution => evolvers}/real/qrte.py (77%) delete mode 100644 qiskit/algorithms/time_evolution/problems/__init__.py rename test/python/algorithms/{time_evolution => evolvers}/__init__.py (100%) rename test/python/algorithms/{time_evolution => evolvers}/problems/__init__.py (100%) rename test/python/algorithms/{time_evolution => evolvers}/problems/test_evolver_problem.py (97%) rename test/python/algorithms/{time_evolution => evolvers}/test_evolver_result.py (96%) diff --git a/qiskit/algorithms/time_evolution/__init__.py b/qiskit/algorithms/evolvers/__init__.py similarity index 56% rename from qiskit/algorithms/time_evolution/__init__.py rename to qiskit/algorithms/evolvers/__init__.py index 805bbc1b8eba..b100b6591927 100644 --- a/qiskit/algorithms/time_evolution/__init__.py +++ b/qiskit/algorithms/evolvers/__init__.py @@ -10,3 +10,19 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """ Quantum Time Evolution package """ + +from qiskit.algorithms.evolvers.evolver import Evolver +from qiskit.algorithms.evolvers.evolver_result import EvolverResult +from qiskit.algorithms.evolvers.imaginary.qite import QITE + +from qiskit.algorithms.evolvers.real.qrte import QRTE + +from qiskit.algorithms.evolvers.evolver_problem import EvolverProblem + +__all__ = [ + "EvolverProblem", + "EvolverResult", + "Evolver", + "QRTE", + "QITE", +] diff --git a/qiskit/algorithms/time_evolution/evolver.py b/qiskit/algorithms/evolvers/evolver.py similarity index 80% rename from qiskit/algorithms/time_evolution/evolver.py rename to qiskit/algorithms/evolvers/evolver.py index a7816e1b0d2b..cb6e1328d29b 100644 --- a/qiskit/algorithms/time_evolution/evolver.py +++ b/qiskit/algorithms/evolvers/evolver.py @@ -10,16 +10,16 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Base class for quantum time evolution.""" +"""Interface for quantum time evolution.""" from abc import ABC, abstractmethod -from qiskit.algorithms.time_evolution.problems.evolver_problem import EvolverProblem -from qiskit.algorithms.time_evolution.evolver_result import EvolverResult +from qiskit.algorithms.evolvers.evolver_problem import EvolverProblem +from qiskit.algorithms.evolvers.evolver_result import EvolverResult class Evolver(ABC): - """Base class for quantum time evolution.""" + """Interface class for quantum time evolution.""" @abstractmethod def evolve(self, evolver_problem: EvolverProblem) -> EvolverResult: diff --git a/qiskit/algorithms/time_evolution/problems/evolver_problem.py b/qiskit/algorithms/evolvers/evolver_problem.py similarity index 100% rename from qiskit/algorithms/time_evolution/problems/evolver_problem.py rename to qiskit/algorithms/evolvers/evolver_problem.py diff --git a/qiskit/algorithms/time_evolution/evolver_result.py b/qiskit/algorithms/evolvers/evolver_result.py similarity index 100% rename from qiskit/algorithms/time_evolution/evolver_result.py rename to qiskit/algorithms/evolvers/evolver_result.py diff --git a/qiskit/algorithms/time_evolution/imaginary/__init__.py b/qiskit/algorithms/evolvers/imaginary/__init__.py similarity index 100% rename from qiskit/algorithms/time_evolution/imaginary/__init__.py rename to qiskit/algorithms/evolvers/imaginary/__init__.py diff --git a/qiskit/algorithms/time_evolution/imaginary/qite.py b/qiskit/algorithms/evolvers/imaginary/qite.py similarity index 76% rename from qiskit/algorithms/time_evolution/imaginary/qite.py rename to qiskit/algorithms/evolvers/imaginary/qite.py index 5a2bb20b75c8..895e4dc8e1a4 100644 --- a/qiskit/algorithms/time_evolution/imaginary/qite.py +++ b/qiskit/algorithms/evolvers/imaginary/qite.py @@ -10,10 +10,10 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Interface for Quantum Imaginary Time Evolution.""" +"""Base class for Quantum Imaginary Time Evolution used for typing purposes.""" from abc import ABC class QITE(ABC): - """Interface for Quantum Imaginary Time Evolution.""" + """Base class for Quantum Imaginary Time Evolution used for typing purposes.""" diff --git a/qiskit/algorithms/time_evolution/real/__init__.py b/qiskit/algorithms/evolvers/real/__init__.py similarity index 100% rename from qiskit/algorithms/time_evolution/real/__init__.py rename to qiskit/algorithms/evolvers/real/__init__.py diff --git a/qiskit/algorithms/time_evolution/real/qrte.py b/qiskit/algorithms/evolvers/real/qrte.py similarity index 77% rename from qiskit/algorithms/time_evolution/real/qrte.py rename to qiskit/algorithms/evolvers/real/qrte.py index a8681710f6ab..2655f3a2b851 100644 --- a/qiskit/algorithms/time_evolution/real/qrte.py +++ b/qiskit/algorithms/evolvers/real/qrte.py @@ -10,10 +10,10 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Interface for Quantum Real Time Evolution.""" +"""Base class for Quantum Real Time Evolution used for typing purposes.""" from abc import ABC class QRTE(ABC): - """Base class for quantum real time evolution.""" + """Base class for Quantum Real Time Evolution used for typing purposes.""" diff --git a/qiskit/algorithms/time_evolution/problems/__init__.py b/qiskit/algorithms/time_evolution/problems/__init__.py deleted file mode 100644 index fdb172d367f0..000000000000 --- a/qiskit/algorithms/time_evolution/problems/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# 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. diff --git a/test/python/algorithms/time_evolution/__init__.py b/test/python/algorithms/evolvers/__init__.py similarity index 100% rename from test/python/algorithms/time_evolution/__init__.py rename to test/python/algorithms/evolvers/__init__.py diff --git a/test/python/algorithms/time_evolution/problems/__init__.py b/test/python/algorithms/evolvers/problems/__init__.py similarity index 100% rename from test/python/algorithms/time_evolution/problems/__init__.py rename to test/python/algorithms/evolvers/problems/__init__.py diff --git a/test/python/algorithms/time_evolution/problems/test_evolver_problem.py b/test/python/algorithms/evolvers/problems/test_evolver_problem.py similarity index 97% rename from test/python/algorithms/time_evolution/problems/test_evolver_problem.py rename to test/python/algorithms/evolvers/problems/test_evolver_problem.py index 069684ef7909..64f718c23b7f 100644 --- a/test/python/algorithms/time_evolution/problems/test_evolver_problem.py +++ b/test/python/algorithms/evolvers/problems/test_evolver_problem.py @@ -14,7 +14,7 @@ import unittest from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit.algorithms.time_evolution.problems.evolver_problem import EvolverProblem +from qiskit.algorithms.evolvers.evolver_problem import EvolverProblem from qiskit.circuit import Parameter from qiskit.opflow import Y, Z, One, X diff --git a/test/python/algorithms/time_evolution/test_evolver_result.py b/test/python/algorithms/evolvers/test_evolver_result.py similarity index 96% rename from test/python/algorithms/time_evolution/test_evolver_result.py rename to test/python/algorithms/evolvers/test_evolver_result.py index 332998a4135a..1d07bf71f55e 100644 --- a/test/python/algorithms/time_evolution/test_evolver_result.py +++ b/test/python/algorithms/evolvers/test_evolver_result.py @@ -13,7 +13,7 @@ import unittest from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit.algorithms.time_evolution.evolver_result import EvolverResult +from qiskit.algorithms.evolvers.evolver_result import EvolverResult from qiskit.opflow import Zero From 5014c618d6498960d9e74a997be073d549738a3a Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 3 Mar 2022 13:35:36 +0100 Subject: [PATCH 057/145] Added Evolvers to algorithms init file. --- qiskit/algorithms/__init__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index ffbb5459d61b..0a7e0f4c2abb 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -92,6 +92,22 @@ NumPyEigensolver +Evolvers +-------- + +Algorithms to evolve quantum states in time. Both real and imaginary time evolution is possible +with algorithms that support them. For machine learning, Quantum Imaginary Time Evolution might be +used to train Quantum Boltzmann Machine Neural Networks for example. + +.. autosummary:: + :toctree: ../stubs/ + :nosignatures: + + EvolverProblem + EvolverResult + Evolver + + Factorizers ----------- From d1cd3e096b30bf280506257d6dc14cdc02aa25b7 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 3 Mar 2022 15:28:40 +0100 Subject: [PATCH 058/145] Fixed missing imports. --- qiskit/algorithms/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index 0a7e0f4c2abb..ac8e3d031e1e 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -191,6 +191,7 @@ """ from .algorithm_result import AlgorithmResult +from .evolvers import EvolverProblem, EvolverResult, Evolver from .variational_algorithm import VariationalAlgorithm, VariationalResult from .amplitude_amplifiers import Grover, GroverResult, AmplificationProblem from .amplitude_estimators import ( @@ -246,6 +247,9 @@ "MaximumLikelihoodAmplitudeEstimationResult", "EstimationProblem", "NumPyEigensolver", + "EvolverProblem", + "EvolverResult", + "Evolver", "LinearSolverResult", "Eigensolver", "EigensolverResult", From d60511960933bef10988bb6b1b2e145463be137f Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 3 Mar 2022 22:10:56 +0100 Subject: [PATCH 059/145] Code refactoring --- qiskit/algorithms/__init__.py | 10 +++++----- qiskit/algorithms/evolvers/__init__.py | 16 ++++++++-------- .../{evolver_problem.py => evolution_problem.py} | 6 +++--- .../{evolver_result.py => evolution_result.py} | 2 +- qiskit/algorithms/evolvers/evolver.py | 8 ++++---- .../imaginary/{qite.py => imaginary_evolver.py} | 2 +- .../evolvers/real/{qrte.py => real_evolver.py} | 2 +- .../algorithms/evolvers/problems/__init__.py | 11 ----------- ...lver_problem.py => test_evolution_problem.py} | 8 ++++---- ...volver_result.py => test_evolution_result.py} | 8 ++++---- 10 files changed, 31 insertions(+), 42 deletions(-) rename qiskit/algorithms/evolvers/{evolver_problem.py => evolution_problem.py} (95%) rename qiskit/algorithms/evolvers/{evolver_result.py => evolution_result.py} (97%) rename qiskit/algorithms/evolvers/imaginary/{qite.py => imaginary_evolver.py} (95%) rename qiskit/algorithms/evolvers/real/{qrte.py => real_evolver.py} (96%) delete mode 100644 test/python/algorithms/evolvers/problems/__init__.py rename test/python/algorithms/evolvers/{problems/test_evolver_problem.py => test_evolution_problem.py} (91%) rename test/python/algorithms/evolvers/{test_evolver_result.py => test_evolution_result.py} (85%) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index ac8e3d031e1e..34aedcc8b308 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -103,8 +103,8 @@ :toctree: ../stubs/ :nosignatures: - EvolverProblem - EvolverResult + EvolutionProblem + EvolutionResult Evolver @@ -191,7 +191,7 @@ """ from .algorithm_result import AlgorithmResult -from .evolvers import EvolverProblem, EvolverResult, Evolver +from .evolvers import EvolutionProblem, EvolutionResult, Evolver from .variational_algorithm import VariationalAlgorithm, VariationalResult from .amplitude_amplifiers import Grover, GroverResult, AmplificationProblem from .amplitude_estimators import ( @@ -247,8 +247,8 @@ "MaximumLikelihoodAmplitudeEstimationResult", "EstimationProblem", "NumPyEigensolver", - "EvolverProblem", - "EvolverResult", + "EvolutionProblem", + "EvolutionResult", "Evolver", "LinearSolverResult", "Eigensolver", diff --git a/qiskit/algorithms/evolvers/__init__.py b/qiskit/algorithms/evolvers/__init__.py index b100b6591927..2b712d393286 100644 --- a/qiskit/algorithms/evolvers/__init__.py +++ b/qiskit/algorithms/evolvers/__init__.py @@ -12,17 +12,17 @@ """ Quantum Time Evolution package """ from qiskit.algorithms.evolvers.evolver import Evolver -from qiskit.algorithms.evolvers.evolver_result import EvolverResult -from qiskit.algorithms.evolvers.imaginary.qite import QITE +from qiskit.algorithms.evolvers.evolution_result import EvolutionResult +from qiskit.algorithms.evolvers.imaginary.imaginary_evolver import ImaginaryEvolver -from qiskit.algorithms.evolvers.real.qrte import QRTE +from qiskit.algorithms.evolvers.real.real_evolver import RealEvolver -from qiskit.algorithms.evolvers.evolver_problem import EvolverProblem +from qiskit.algorithms.evolvers.evolution_problem import EvolutionProblem __all__ = [ - "EvolverProblem", - "EvolverResult", + "EvolutionProblem", + "EvolutionResult", "Evolver", - "QRTE", - "QITE", + "RealEvolver", + "ImaginaryEvolver", ] diff --git a/qiskit/algorithms/evolvers/evolver_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py similarity index 95% rename from qiskit/algorithms/evolvers/evolver_problem.py rename to qiskit/algorithms/evolvers/evolution_problem.py index 8baf17a766fe..a13c62f2fcea 100644 --- a/qiskit/algorithms/evolvers/evolver_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Evolver problem class.""" +"""Evolution problem class.""" from typing import Union, Optional, Dict @@ -20,8 +20,8 @@ from qiskit.opflow import OperatorBase, StateFn -class EvolverProblem: - """Evolver problem class.""" +class EvolutionProblem: + """Evolution problem class.""" def __init__( self, diff --git a/qiskit/algorithms/evolvers/evolver_result.py b/qiskit/algorithms/evolvers/evolution_result.py similarity index 97% rename from qiskit/algorithms/evolvers/evolver_result.py rename to qiskit/algorithms/evolvers/evolution_result.py index 214e4b29d914..d8f459272fbf 100644 --- a/qiskit/algorithms/evolvers/evolver_result.py +++ b/qiskit/algorithms/evolvers/evolution_result.py @@ -18,7 +18,7 @@ from qiskit.opflow import StateFn -class EvolverResult(AlgorithmResult): +class EvolutionResult(AlgorithmResult): """Class for holding evolution result.""" def __init__( diff --git a/qiskit/algorithms/evolvers/evolver.py b/qiskit/algorithms/evolvers/evolver.py index cb6e1328d29b..e8344c01d584 100644 --- a/qiskit/algorithms/evolvers/evolver.py +++ b/qiskit/algorithms/evolvers/evolver.py @@ -14,20 +14,20 @@ from abc import ABC, abstractmethod -from qiskit.algorithms.evolvers.evolver_problem import EvolverProblem -from qiskit.algorithms.evolvers.evolver_result import EvolverResult +from qiskit.algorithms.evolvers.evolution_problem import EvolutionProblem +from qiskit.algorithms.evolvers.evolution_result import EvolutionResult class Evolver(ABC): """Interface class for quantum time evolution.""" @abstractmethod - def evolve(self, evolver_problem: EvolverProblem) -> EvolverResult: + def evolve(self, evolver_problem: EvolutionProblem) -> EvolutionResult: """ Evolves an initial state in the evolution_problem according to a Hamiltonian provided. Args: - evolver_problem: EvolverProblem instance that includes definition of an evolution + evolver_problem: EvolutionProblem instance that includes definition of an evolution problem. Returns: diff --git a/qiskit/algorithms/evolvers/imaginary/qite.py b/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py similarity index 95% rename from qiskit/algorithms/evolvers/imaginary/qite.py rename to qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py index 895e4dc8e1a4..fa398a263c3a 100644 --- a/qiskit/algorithms/evolvers/imaginary/qite.py +++ b/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py @@ -15,5 +15,5 @@ from abc import ABC -class QITE(ABC): +class ImaginaryEvolver(ABC): """Base class for Quantum Imaginary Time Evolution used for typing purposes.""" diff --git a/qiskit/algorithms/evolvers/real/qrte.py b/qiskit/algorithms/evolvers/real/real_evolver.py similarity index 96% rename from qiskit/algorithms/evolvers/real/qrte.py rename to qiskit/algorithms/evolvers/real/real_evolver.py index 2655f3a2b851..8e6e35e76552 100644 --- a/qiskit/algorithms/evolvers/real/qrte.py +++ b/qiskit/algorithms/evolvers/real/real_evolver.py @@ -15,5 +15,5 @@ from abc import ABC -class QRTE(ABC): +class RealEvolver(ABC): """Base class for Quantum Real Time Evolution used for typing purposes.""" diff --git a/test/python/algorithms/evolvers/problems/__init__.py b/test/python/algorithms/evolvers/problems/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/test/python/algorithms/evolvers/problems/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/test/python/algorithms/evolvers/problems/test_evolver_problem.py b/test/python/algorithms/evolvers/test_evolution_problem.py similarity index 91% rename from test/python/algorithms/evolvers/problems/test_evolver_problem.py rename to test/python/algorithms/evolvers/test_evolution_problem.py index 64f718c23b7f..cdd472247045 100644 --- a/test/python/algorithms/evolvers/problems/test_evolver_problem.py +++ b/test/python/algorithms/evolvers/test_evolution_problem.py @@ -14,12 +14,12 @@ import unittest from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit.algorithms.evolvers.evolver_problem import EvolverProblem +from qiskit.algorithms.evolvers.evolution_problem import EvolutionProblem from qiskit.circuit import Parameter from qiskit.opflow import Y, Z, One, X -class TestEvolverProblem(QiskitAlgorithmsTestCase): +class TestEvolutionProblem(QiskitAlgorithmsTestCase): """Test evolver problem class.""" def test_init_default(self): @@ -28,7 +28,7 @@ def test_init_default(self): time = 2.5 initial_state = One - evo_problem = EvolverProblem(hamiltonian, time, initial_state) + evo_problem = EvolutionProblem(hamiltonian, time, initial_state) expected_hamiltonian = Y expected_time = 2.5 @@ -53,7 +53,7 @@ def test_init_all(self): aux_operators = [X, Y] hamiltonian_value_dict = {t_parameter: 3.2} - evo_problem = EvolverProblem( + evo_problem = EvolutionProblem( hamiltonian, time, initial_state, aux_operators, t_parameter, hamiltonian_value_dict ) diff --git a/test/python/algorithms/evolvers/test_evolver_result.py b/test/python/algorithms/evolvers/test_evolution_result.py similarity index 85% rename from test/python/algorithms/evolvers/test_evolver_result.py rename to test/python/algorithms/evolvers/test_evolution_result.py index 1d07bf71f55e..5500b283a1cb 100644 --- a/test/python/algorithms/evolvers/test_evolver_result.py +++ b/test/python/algorithms/evolvers/test_evolution_result.py @@ -13,17 +13,17 @@ import unittest from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit.algorithms.evolvers.evolver_result import EvolverResult +from qiskit.algorithms.evolvers.evolution_result import EvolutionResult from qiskit.opflow import Zero -class TestEvolverResult(QiskitAlgorithmsTestCase): +class TestEvolutionResult(QiskitAlgorithmsTestCase): """Class for testing evolution result and relevant metadata.""" def test_init_state(self): """Tests that a class is initialized correctly with an evolved_state.""" evolved_state = Zero - evo_result = EvolverResult(evolved_state=evolved_state) + evo_result = EvolutionResult(evolved_state=evolved_state) expected_state = Zero expected_aux_ops_evaluated = None @@ -35,7 +35,7 @@ def test_init_observable(self): """Tests that a class is initialized correctly with an evolved_observable.""" evolved_state = Zero evolved_aux_ops_evaluated = [(5j, 5j), (1.0, 8j), (5 + 1j, 6 + 1j)] - evo_result = EvolverResult(evolved_state, evolved_aux_ops_evaluated) + evo_result = EvolutionResult(evolved_state, evolved_aux_ops_evaluated) expected_state = Zero expected_aux_ops_evaluated = [(5j, 5j), (1.0, 8j), (5 + 1j, 6 + 1j)] From c6a04e0cb9185df76de878fc9c346913b0dff191 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 4 Mar 2022 13:23:26 +0100 Subject: [PATCH 060/145] Fixed cyclic imports. --- qiskit/algorithms/__init__.py | 11 ++++------- qiskit/algorithms/evolvers/__init__.py | 16 +++++++--------- qiskit/algorithms/evolvers/evolution_result.py | 4 ++-- qiskit/algorithms/evolvers/evolver.py | 4 ++-- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index 34aedcc8b308..ed23d5fa96ed 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2021. +# (C) Copyright IBM 2018, 2021, 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 @@ -103,10 +103,8 @@ :toctree: ../stubs/ :nosignatures: - EvolutionProblem EvolutionResult - Evolver - + EvolutionProblem Factorizers ----------- @@ -191,7 +189,7 @@ """ from .algorithm_result import AlgorithmResult -from .evolvers import EvolutionProblem, EvolutionResult, Evolver +from .evolvers import EvolutionResult, EvolutionProblem from .variational_algorithm import VariationalAlgorithm, VariationalResult from .amplitude_amplifiers import Grover, GroverResult, AmplificationProblem from .amplitude_estimators import ( @@ -247,9 +245,8 @@ "MaximumLikelihoodAmplitudeEstimationResult", "EstimationProblem", "NumPyEigensolver", - "EvolutionProblem", "EvolutionResult", - "Evolver", + "EvolutionProblem", "LinearSolverResult", "Eigensolver", "EigensolverResult", diff --git a/qiskit/algorithms/evolvers/__init__.py b/qiskit/algorithms/evolvers/__init__.py index 2b712d393286..605d8e4c1c33 100644 --- a/qiskit/algorithms/evolvers/__init__.py +++ b/qiskit/algorithms/evolvers/__init__.py @@ -11,18 +11,16 @@ # that they have been altered from the originals. """ Quantum Time Evolution package """ -from qiskit.algorithms.evolvers.evolver import Evolver -from qiskit.algorithms.evolvers.evolution_result import EvolutionResult -from qiskit.algorithms.evolvers.imaginary.imaginary_evolver import ImaginaryEvolver - -from qiskit.algorithms.evolvers.real.real_evolver import RealEvolver - -from qiskit.algorithms.evolvers.evolution_problem import EvolutionProblem +from .evolver import Evolver +from .evolution_result import EvolutionResult +from .real.real_evolver import RealEvolver +from .imaginary.imaginary_evolver import ImaginaryEvolver +from .evolution_problem import EvolutionProblem __all__ = [ - "EvolutionProblem", - "EvolutionResult", "Evolver", + "EvolutionResult", "RealEvolver", "ImaginaryEvolver", + "EvolutionProblem", ] diff --git a/qiskit/algorithms/evolvers/evolution_result.py b/qiskit/algorithms/evolvers/evolution_result.py index d8f459272fbf..eb3d0b2f543d 100644 --- a/qiskit/algorithms/evolvers/evolution_result.py +++ b/qiskit/algorithms/evolvers/evolution_result.py @@ -13,9 +13,9 @@ from typing import Optional, Union, Tuple from qiskit import QuantumCircuit -from qiskit.algorithms import AlgorithmResult -from qiskit.algorithms.eigen_solvers.eigen_solver import ListOrDict from qiskit.opflow import StateFn +from qiskit.algorithms.algorithm_result import AlgorithmResult +from ..eigen_solvers.eigen_solver import ListOrDict class EvolutionResult(AlgorithmResult): diff --git a/qiskit/algorithms/evolvers/evolver.py b/qiskit/algorithms/evolvers/evolver.py index e8344c01d584..101af197493e 100644 --- a/qiskit/algorithms/evolvers/evolver.py +++ b/qiskit/algorithms/evolvers/evolver.py @@ -14,8 +14,8 @@ from abc import ABC, abstractmethod -from qiskit.algorithms.evolvers.evolution_problem import EvolutionProblem -from qiskit.algorithms.evolvers.evolution_result import EvolutionResult +from .evolution_problem import EvolutionProblem +from .evolution_result import EvolutionResult class Evolver(ABC): From 6708db1d125b31036dc7fae237ccd6155deac1b9 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 4 Mar 2022 13:33:45 +0100 Subject: [PATCH 061/145] Extracted ListOrDict. --- .../algorithms/eigen_solvers/eigen_solver.py | 8 +++----- .../eigen_solvers/numpy_eigen_solver.py | 3 ++- .../algorithms/evolvers/evolution_problem.py | 2 +- qiskit/algorithms/evolvers/evolution_result.py | 2 +- qiskit/algorithms/list_or_dict.py | 18 ++++++++++++++++++ .../minimum_eigen_solver.py | 8 +++----- .../numpy_minimum_eigen_solver.py | 3 ++- qiskit/algorithms/minimum_eigen_solvers/vqe.py | 3 ++- 8 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 qiskit/algorithms/list_or_dict.py diff --git a/qiskit/algorithms/eigen_solvers/eigen_solver.py b/qiskit/algorithms/eigen_solvers/eigen_solver.py index bdb8933ac5ea..28ca3e9d1f8b 100644 --- a/qiskit/algorithms/eigen_solvers/eigen_solver.py +++ b/qiskit/algorithms/eigen_solvers/eigen_solver.py @@ -13,15 +13,13 @@ """The Eigensolver interface""" from abc import ABC, abstractmethod -from typing import Dict, Optional, List, Union, Tuple, TypeVar +from typing import Optional, List, Tuple import numpy as np + from qiskit.opflow import OperatorBase from ..algorithm_result import AlgorithmResult - -# Introduced new type to maintain readability. -_T = TypeVar("_T") # Pylint does not allow single character class names. -ListOrDict = Union[List[Optional[_T]], Dict[str, _T]] +from ..list_or_dict import ListOrDict class Eigensolver(ABC): diff --git a/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py b/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py index c30504b676d3..4abbe7ed4a26 100755 --- a/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py +++ b/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py @@ -21,7 +21,8 @@ from qiskit.opflow import I, ListOp, OperatorBase, StateFn from qiskit.utils.validation import validate_min from ..exceptions import AlgorithmError -from .eigen_solver import Eigensolver, EigensolverResult, ListOrDict +from .eigen_solver import Eigensolver, EigensolverResult +from ..list_or_dict import ListOrDict logger = logging.getLogger(__name__) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index a13c62f2fcea..bb9ef7efcdad 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -15,7 +15,7 @@ from typing import Union, Optional, Dict from qiskit import QuantumCircuit -from qiskit.algorithms.eigen_solvers.eigen_solver import ListOrDict +from qiskit.algorithms.list_or_dict import ListOrDict from qiskit.circuit import Parameter from qiskit.opflow import OperatorBase, StateFn diff --git a/qiskit/algorithms/evolvers/evolution_result.py b/qiskit/algorithms/evolvers/evolution_result.py index eb3d0b2f543d..444f03c7d8c9 100644 --- a/qiskit/algorithms/evolvers/evolution_result.py +++ b/qiskit/algorithms/evolvers/evolution_result.py @@ -13,9 +13,9 @@ from typing import Optional, Union, Tuple from qiskit import QuantumCircuit +from qiskit.algorithms.list_or_dict import ListOrDict from qiskit.opflow import StateFn from qiskit.algorithms.algorithm_result import AlgorithmResult -from ..eigen_solvers.eigen_solver import ListOrDict class EvolutionResult(AlgorithmResult): diff --git a/qiskit/algorithms/list_or_dict.py b/qiskit/algorithms/list_or_dict.py new file mode 100644 index 000000000000..95314dd79a3b --- /dev/null +++ b/qiskit/algorithms/list_or_dict.py @@ -0,0 +1,18 @@ +# 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. + +"""Introduced new type to maintain readability.""" + +from typing import TypeVar, List, Union, Optional, Dict + +_T = TypeVar("_T") # Pylint does not allow single character class names. +ListOrDict = Union[List[Optional[_T]], Dict[str, _T]] diff --git a/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py b/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py index 545cf289643b..7cdd245b7076 100644 --- a/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py +++ b/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py @@ -13,15 +13,13 @@ """The Minimum Eigensolver interface""" from abc import ABC, abstractmethod -from typing import Dict, Optional, List, Union, Tuple, TypeVar +from typing import Optional, Tuple import numpy as np + from qiskit.opflow import OperatorBase from ..algorithm_result import AlgorithmResult - -# Introduced new type to maintain readability. -_T = TypeVar("_T") # Pylint does not allow single character class names. -ListOrDict = Union[List[Optional[_T]], Dict[str, _T]] +from ..list_or_dict import ListOrDict class MinimumEigensolver(ABC): diff --git a/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py b/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py index 369485976cdf..5515ba92d745 100644 --- a/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py +++ b/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py @@ -18,7 +18,8 @@ from qiskit.opflow import OperatorBase from ..eigen_solvers.numpy_eigen_solver import NumPyEigensolver -from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult, ListOrDict +from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult +from ..list_or_dict import ListOrDict logger = logging.getLogger(__name__) diff --git a/qiskit/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/algorithms/minimum_eigen_solvers/vqe.py index 83a09e9b9521..6af9d9c6d9d3 100755 --- a/qiskit/algorithms/minimum_eigen_solvers/vqe.py +++ b/qiskit/algorithms/minimum_eigen_solvers/vqe.py @@ -40,9 +40,10 @@ from qiskit.utils.backend_utils import is_aer_provider from qiskit.utils.deprecation import deprecate_function from qiskit.utils import QuantumInstance, algorithm_globals +from ..list_or_dict import ListOrDict from ..optimizers import Optimizer, SLSQP from ..variational_algorithm import VariationalAlgorithm, VariationalResult -from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult, ListOrDict +from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult from ..exceptions import AlgorithmError logger = logging.getLogger(__name__) From 22f886ceddf256ea3337b8968aea0657b5565ba8 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 4 Mar 2022 15:19:29 +0100 Subject: [PATCH 062/145] Code refactoring. --- qiskit/algorithms/__init__.py | 6 +++++- test/python/algorithms/evolvers/__init__.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index ed23d5fa96ed..77df82c3f159 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -103,6 +103,8 @@ :toctree: ../stubs/ :nosignatures: + RealEvolver + ImaginaryEvolver EvolutionResult EvolutionProblem @@ -189,7 +191,7 @@ """ from .algorithm_result import AlgorithmResult -from .evolvers import EvolutionResult, EvolutionProblem +from .evolvers import EvolutionResult, EvolutionProblem, RealEvolver, ImaginaryEvolver from .variational_algorithm import VariationalAlgorithm, VariationalResult from .amplitude_amplifiers import Grover, GroverResult, AmplificationProblem from .amplitude_estimators import ( @@ -245,6 +247,8 @@ "MaximumLikelihoodAmplitudeEstimationResult", "EstimationProblem", "NumPyEigensolver", + "RealEvolver", + "ImaginaryEvolver", "EvolutionResult", "EvolutionProblem", "LinearSolverResult", diff --git a/test/python/algorithms/evolvers/__init__.py b/test/python/algorithms/evolvers/__init__.py index 96c0cf22bec9..fdb172d367f0 100644 --- a/test/python/algorithms/evolvers/__init__.py +++ b/test/python/algorithms/evolvers/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (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 From eb9ef8909857c57310cf79432bcc9c3eac7f4de8 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 7 Mar 2022 08:43:14 +0100 Subject: [PATCH 063/145] Code refactoring. --- qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py | 4 +++- qiskit/algorithms/evolvers/real/real_evolver.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py b/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py index fa398a263c3a..2516e0767f41 100644 --- a/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py +++ b/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py @@ -14,6 +14,8 @@ from abc import ABC +from qiskit.algorithms.evolvers import Evolver -class ImaginaryEvolver(ABC): + +class ImaginaryEvolver(ABC, Evolver): """Base class for Quantum Imaginary Time Evolution used for typing purposes.""" diff --git a/qiskit/algorithms/evolvers/real/real_evolver.py b/qiskit/algorithms/evolvers/real/real_evolver.py index 8e6e35e76552..34314b4bca35 100644 --- a/qiskit/algorithms/evolvers/real/real_evolver.py +++ b/qiskit/algorithms/evolvers/real/real_evolver.py @@ -14,6 +14,8 @@ from abc import ABC +from qiskit.algorithms.evolvers import Evolver -class RealEvolver(ABC): + +class RealEvolver(ABC, Evolver): """Base class for Quantum Real Time Evolution used for typing purposes.""" From 897ac19b2f9e49f5631ef3be66f014c3ad88e1a5 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 7 Mar 2022 08:53:18 +0100 Subject: [PATCH 064/145] Fixed release note. --- .../notes/time-evo-framework-9d58827bdbbebd62.yaml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml b/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml index 5c7bbf513b3e..208c7c372066 100644 --- a/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml +++ b/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml @@ -2,10 +2,13 @@ features: - | Interfaces for the unified framework for Quantum Time Evolution are introduced. - :py:class:`~qiskit.algorithms.time_evolution.evolution_base` interface serves as a base for - any time evolution algorithm for evolving quantum states or observables, including - time-dependent Hamiltonians. :py:class:`~qiskit.algorithms.time_evolution.imaginary.qite` and - :py:class:`~qiskit.algorithms.time_evolution.imaginary.qrte` are derived interfaces for + :class:`~qiskit.algorithms.time_evolution.EvolutionProblem` defines an evolution problem and + can be passed to any evolution algorithm available in the framework. + :class:`~qiskit.algorithms.time_evolution.Evolver` interface serves as a base for + any time evolution algorithm for evolving quantum states, including evolutions based on + time-dependent Hamiltonians. + :class:`~qiskit.algorithms.time_evolution.imaginary.ImaginaryEvolver` and + :class:`~qiskit.algorithms.time_evolution.real.RealEvolver` are derived interfaces for imaginary and real time evolution cases respectively. - :py:class:`~qiskit.algorithms.time_evolution.evolution_result` is introduced as a result object + :class:`~qiskit.algorithms.time_evolution.EvolutionResult` is introduced as a result object for quantum time evolution algorithms. From e0e2919b13ab34576ea66aa97e4f488fa471bb85 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 7 Mar 2022 12:43:38 +0100 Subject: [PATCH 065/145] Fixed inheritance order. --- qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py | 4 ++-- qiskit/algorithms/evolvers/real/real_evolver.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py b/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py index 2516e0767f41..67c2d504574e 100644 --- a/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py +++ b/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py @@ -14,8 +14,8 @@ from abc import ABC -from qiskit.algorithms.evolvers import Evolver +from .. import Evolver -class ImaginaryEvolver(ABC, Evolver): +class ImaginaryEvolver(Evolver, ABC): """Base class for Quantum Imaginary Time Evolution used for typing purposes.""" diff --git a/qiskit/algorithms/evolvers/real/real_evolver.py b/qiskit/algorithms/evolvers/real/real_evolver.py index 34314b4bca35..5197b1d69d6a 100644 --- a/qiskit/algorithms/evolvers/real/real_evolver.py +++ b/qiskit/algorithms/evolvers/real/real_evolver.py @@ -14,8 +14,8 @@ from abc import ABC -from qiskit.algorithms.evolvers import Evolver +from .. import Evolver -class RealEvolver(ABC, Evolver): +class RealEvolver(Evolver, ABC): """Base class for Quantum Real Time Evolution used for typing purposes.""" From 73d007931549c6172f4e1972f7bc19a565ec2cc3 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 7 Mar 2022 13:40:54 +0100 Subject: [PATCH 066/145] Updated code to the latest time evolution interface. --- qiskit/algorithms/evolvers/evolver.py | 4 +- .../algorithms/evolvers/imaginary/__init__.py | 11 --- .../evolvers/imaginary/imaginary_evolver.py | 21 ------ .../real}/trotterization/__init__.py | 2 +- .../trotterization/trotter_ops_validator.py | 0 .../real}/trotterization/trotter_qrte.py | 72 ++++++------------- .../quantum_time_evolution/evolution_base.py | 50 ------------- .../real/implementations/__init__.py | 11 --- .../quantum_time_evolution/real/qrte.py | 39 ---------- .../test_trotter_ops_validator.py | 3 +- .../trotterization/test_trotter_qrte.py | 50 ++++++------- 11 files changed, 48 insertions(+), 215 deletions(-) delete mode 100644 qiskit/algorithms/evolvers/imaginary/__init__.py delete mode 100644 qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py rename qiskit/algorithms/{quantum_time_evolution/real/implementations => evolvers/real}/trotterization/__init__.py (91%) rename qiskit/algorithms/{quantum_time_evolution/real/implementations => evolvers/real}/trotterization/trotter_ops_validator.py (100%) rename qiskit/algorithms/{quantum_time_evolution/real/implementations => evolvers/real}/trotterization/trotter_qrte.py (62%) delete mode 100644 qiskit/algorithms/quantum_time_evolution/evolution_base.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/real/implementations/__init__.py delete mode 100644 qiskit/algorithms/quantum_time_evolution/real/qrte.py diff --git a/qiskit/algorithms/evolvers/evolver.py b/qiskit/algorithms/evolvers/evolver.py index 101af197493e..15463c09097a 100644 --- a/qiskit/algorithms/evolvers/evolver.py +++ b/qiskit/algorithms/evolvers/evolver.py @@ -14,8 +14,8 @@ from abc import ABC, abstractmethod -from .evolution_problem import EvolutionProblem -from .evolution_result import EvolutionResult +from . import EvolutionProblem +from . import EvolutionResult class Evolver(ABC): diff --git a/qiskit/algorithms/evolvers/imaginary/__init__.py b/qiskit/algorithms/evolvers/imaginary/__init__.py deleted file mode 100644 index b3ac36d2a6d9..000000000000 --- a/qiskit/algorithms/evolvers/imaginary/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 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. diff --git a/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py b/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py deleted file mode 100644 index 67c2d504574e..000000000000 --- a/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py +++ /dev/null @@ -1,21 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 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. - -"""Base class for Quantum Imaginary Time Evolution used for typing purposes.""" - -from abc import ABC - -from .. import Evolver - - -class ImaginaryEvolver(Evolver, ABC): - """Base class for Quantum Imaginary Time Evolution used for typing purposes.""" diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py b/qiskit/algorithms/evolvers/real/trotterization/__init__.py similarity index 91% rename from qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py rename to qiskit/algorithms/evolvers/real/trotterization/__init__.py index fc6912b00177..77ca4a71d5d1 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py +++ b/qiskit/algorithms/evolvers/real/trotterization/__init__.py @@ -18,7 +18,7 @@ a Hamiltonian is time-dependent via t_param = Parameter("t")) and/or w.r.t. parameters present in a Hamiltonian using an expectation value through a custom observable.""" -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( +from qiskit.algorithms.evolvers.real.trotterization.trotter_qrte import ( TrotterQrte, ) diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py similarity index 100% rename from qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_ops_validator.py rename to qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py similarity index 62% rename from qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py rename to qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 9b39500872ff..4e271748eda2 100644 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -14,27 +14,22 @@ from typing import Union, Dict -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization\ - .trotter_ops_validator import ( - _validate_input, +from ... import EvolutionProblem, EvolutionResult, RealEvolver +from trotter_ops_validator import ( _is_op_bound, ) -from qiskit.algorithms.quantum_time_evolution.real.qrte import Qrte from qiskit.circuit import Parameter from qiskit.opflow import ( OperatorBase, - StateFn, SummedOp, - PauliSumOp, PauliOp, CircuitOp, ) from qiskit.circuit.library import PauliEvolutionGate -from qiskit.quantum_info import Pauli from qiskit.synthesis import ProductFormula, LieTrotter -class TrotterQrte(Qrte): +class TrotterQrte(RealEvolver): """ Class for performing Quantum Real Time Evolution using Trotterization. Type of Trotterization is defined by a ProductFormula provided. @@ -43,15 +38,17 @@ class TrotterQrte(Qrte): .. jupyter-execute:: from qiskit.opflow import X, Y, Zero - from qiskit.algorithms.quantum_time_evolution.real.implementations.\ + from qiskit.algorithms import EvolutionProblem, EvolutionResult + from qiskit.algorithms.evolvers.real.implementations.\ trotterization.trotter_qrte import TrotterQrte operator = X + Z - # LieTrotter with 1 rep - trotter_qrte = TrotterQrte() initial_state = Zero time = 1 - evolved_state = trotter_qrte.evolve(operator, time, initial_state) + evolution_problem = EvolutionProblem(operator, 1, initial_state) + # LieTrotter with 1 rep + trotter_qrte = TrotterQrte(evolution_problem) + evolved_state = trotter_qrte.evolve().evolved_state """ def __init__(self, product_formula: ProductFormula = LieTrotter()) -> None: @@ -62,63 +59,41 @@ def __init__(self, product_formula: ProductFormula = LieTrotter()) -> None: """ self.product_formula = product_formula - def evolve( - self, - hamiltonian: Union[Pauli, PauliOp, PauliSumOp], - time: float, - initial_state: StateFn = None, - observable: OperatorBase = None, - t_param: Parameter = None, - hamiltonian_value_dict: Dict[Parameter, Union[float, complex]] = None, - ) -> StateFn: + # TODO aux ops + def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: """ - Evolves a quantum state or an observable for a given time using the Trotterization method + Evolves a quantum state for a given time using the Trotterization method based on a product formula provided. Time-dependent Hamiltonians are not yet supported. Args: - hamiltonian: - The operator to evolve. Can also be provided as list of non-commuting - operators where the elements are sums of commuting operators. - For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. - time: Total time of evolution. - initial_state: If interested in a quantum state time evolution, a quantum state to be - evolved. - observable: If interested in a quantum observable time evolution, a quantum observable - to be evolved. - t_param: Not supported by this algorithm. - hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values. + evolution_problem: Instance defining evolution problem. Returns: - The evolved hamiltonian applied to either an initial state or an observable. + Evolution result that includes an evolved state. Raises: ValueError: If t_param is not set to None (feature not currently supported). """ - if t_param is not None: + if evolution_problem.t_param is not None: raise ValueError( "TrotterQrte does not accept a time dependent hamiltonian," "t_param should be set to None." ) - hamiltonian = self._try_binding_params(hamiltonian, hamiltonian_value_dict) - _validate_input(initial_state, observable) + hamiltonian = self._try_binding_params( + evolution_problem.hamiltonian, evolution_problem.hamiltonian_value_dict + ) # the evolution gate evolution_gate = CircuitOp( - PauliEvolutionGate(hamiltonian, time, synthesis=self.product_formula) + PauliEvolutionGate(hamiltonian, evolution_problem.time, synthesis=self.product_formula) ) - if initial_state is not None: - return (evolution_gate @ initial_state).eval() - if observable is not None: - # TODO Temporary patch due to terra bug - evolution_gate_adjoint = CircuitOp( - PauliEvolutionGate(hamiltonian[::-1], -time, synthesis=self.product_formula) - ) - return evolution_gate_adjoint @ observable @ evolution_gate + if evolution_problem.initial_state is not None: + evolved_state = (evolution_gate @ evolution_problem.initial_state).eval() + return EvolutionResult(evolved_state) - raise ValueError("Either initial_state or observable must be provided.") + raise ValueError("initial_state must be provided.") @staticmethod def _try_binding_params( @@ -169,4 +144,3 @@ def _try_binding_params( f"Provided a Hamiltonian of an unsupported type: {type(hamiltonian)}. Only " f"SummedOp, PauliOp, and OperatorBase base are supported by TrotterQrte." ) - diff --git a/qiskit/algorithms/quantum_time_evolution/evolution_base.py b/qiskit/algorithms/quantum_time_evolution/evolution_base.py deleted file mode 100644 index 495c318d835d..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/evolution_base.py +++ /dev/null @@ -1,50 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. - -"""Base class for quantum time evolution.""" - -from abc import ABC, abstractmethod -from typing import Union, List - -from qiskit.circuit import Parameter -from qiskit.opflow import OperatorBase, StateFn, Gradient - - -class EvolutionBase(ABC): - """Base class for quantum time evolution.""" - - @abstractmethod - def evolve( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn = None, - observable: OperatorBase = None, - t_param: Parameter = None, - hamiltonian_value_dict: [Parameter, Union[float, complex]] = None, - ): - """ - Evolves an initial state according to a Hamiltonian provided. - Args: - hamiltonian: - ⟨ψ(ω)|H|ψ(ω)〉 - Operator used vor time evolution. - time: Total time of evolution. - initial_state: Quantum state to be evolved. - observable: Observable to be evolved. - t_param: Time parameter in case of a time-dependent Hamiltonian. - hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values, including the t_param. - # TODO do we allow binding t_param here? - """ - raise NotImplementedError() - diff --git a/qiskit/algorithms/quantum_time_evolution/real/implementations/__init__.py b/qiskit/algorithms/quantum_time_evolution/real/implementations/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/real/implementations/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/qiskit/algorithms/quantum_time_evolution/real/qrte.py b/qiskit/algorithms/quantum_time_evolution/real/qrte.py deleted file mode 100644 index 2c3acb528d55..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/real/qrte.py +++ /dev/null @@ -1,39 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. - -"""Interface for Quantum Real Time Evolution.""" - -from abc import abstractmethod - -from qiskit.algorithms.quantum_time_evolution.evolution_base import EvolutionBase -from qiskit.opflow import StateFn, OperatorBase, Gradient - - -class Qrte(EvolutionBase): - """Base class for quantum real time evolution.""" - - @abstractmethod - def evolve( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn = None, - observable: OperatorBase = None, - t_param=None, - hamiltonian_value_dict=None, - ): - """ - Performs Quantum Real Time Evolution on an initial state according to a Hamiltonian - provided. - """ - raise NotImplementedError() - diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py index ffc2551e7e41..126f26401298 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py @@ -18,8 +18,7 @@ from ddt import ddt, data, unpack import numpy as np -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.\ - trotter_ops_validator import ( +from qiskit.algorithms.evolvers.real.trotterization.trotter_ops_validator import ( _validate_hamiltonian_form, _is_pauli_lin_single_param, ) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 928eaf3fa2ca..7105be0b4483 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -14,13 +14,13 @@ import unittest -from test.python.opflow import QiskitOpflowTestCase from ddt import ddt, data import numpy as np from numpy.testing import assert_raises from scipy.linalg import expm -from qiskit.algorithms.quantum_time_evolution.real.implementations.trotterization.trotter_qrte import ( +from qiskit.algorithms import EvolutionProblem +from qiskit.algorithms.evolvers.real.trotterization.trotter_qrte import ( TrotterQrte, ) from qiskit.quantum_info import Statevector @@ -37,6 +37,7 @@ Y, ) from qiskit.synthesis import SuzukiTrotter, QDrift +from test.python.opflow import QiskitOpflowTestCase @ddt @@ -49,17 +50,18 @@ def test_trotter_qrte_trotter(self): # LieTrotter with 1 rep trotter_qrte = TrotterQrte() initial_state = Zero - evolved_state = trotter_qrte.evolve(operator, 1, initial_state) + evolution_problem = EvolutionProblem(operator, 1, initial_state) + evolution_result = trotter_qrte.evolve(evolution_problem) # Calculate the expected state expected_state = ( expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - np.testing.assert_equal(evolved_state, expected_evolved_state) + np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) @data((X ^ Y) + (Y ^ X), (Z ^ Z) + (Z ^ I) + (I ^ Z), Y ^ Y) - def test_trotter_qrte_trotter_2(self, op): + def test_trotter_qrte_trotter_2(self, operator): """Test for trotter qrte.""" # LieTrotter with 1 rep trotter_qrte = TrotterQrte() @@ -67,26 +69,12 @@ def test_trotter_qrte_trotter_2(self, op): # Calculate the expected state expected_state = initial_state.to_matrix() - expected_state = expm(-1j * op.to_matrix()) @ expected_state + expected_state = expm(-1j * operator.to_matrix()) @ expected_state expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2, 2))) - evolved_state = trotter_qrte.evolve(op, 1, initial_state) - np.testing.assert_equal(evolved_state, expected_evolved_state) - - def test_trotter_qrte_trotter_observable(self): - """Test for trotter qrte with an observable.""" - operator = X + Z - # LieTrotter with 1 rep - trotter_qrte = TrotterQrte() - observable = X - evolved_observable = trotter_qrte.evolve(operator, 1, observable=observable) - evolved_observable = evolved_observable.to_matrix_op() - # Calculate the expected operator - expected_op = expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) - expected_evolved_observable = MatrixOp( - expected_op.conj().T @ observable.to_matrix() @ expected_op - ) - np.testing.assert_equal(evolved_observable, expected_evolved_observable) + evolution_problem = EvolutionProblem(operator, 1, initial_state) + evolution_result = trotter_qrte.evolve(evolution_problem) + np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) def test_trotter_qrte_suzuki(self): """Test for trotter qrte with Suzuki.""" @@ -94,7 +82,8 @@ def test_trotter_qrte_suzuki(self): # 2nd order Suzuki with 1 rep trotter_qrte = TrotterQrte(SuzukiTrotter()) initial_state = Zero - evolved_state = trotter_qrte.evolve(operator, 1, initial_state) + evolution_problem = EvolutionProblem(operator, 1, initial_state) + evolution_result = trotter_qrte.evolve(evolution_problem) # Calculate the expected state expected_state = ( expm(-1j * X.to_matrix() * 0.5) @@ -104,7 +93,7 @@ def test_trotter_qrte_suzuki(self): ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - np.testing.assert_equal(evolved_state, expected_evolved_state) + np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) def test_trotter_qrte_qdrift(self): """Test for trotter qrte with QDrift.""" @@ -113,7 +102,8 @@ def test_trotter_qrte_qdrift(self): # QDrift with one repetition trotter_qrte = TrotterQrte(QDrift()) initial_state = Zero - evolved_state = trotter_qrte.evolve(operator, 1, initial_state) + evolution_problem = EvolutionProblem(operator, 1, initial_state) + evolution_result = trotter_qrte.evolve(evolution_problem) sampled_ops = [Z, X, X, X, Z, Z, Z, Z] evo_time = 0.25 # Calculate the expected state @@ -122,7 +112,7 @@ def test_trotter_qrte_qdrift(self): expected_state = expm(-1j * op.to_matrix() * evo_time) @ expected_state expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - np.testing.assert_equal(evolved_state, expected_evolved_state) + np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) def test_trotter_qrte_trotter_binding_missing_dict(self): """Test for trotter qrte with binding and missing dictionary..""" @@ -130,8 +120,9 @@ def test_trotter_qrte_trotter_binding_missing_dict(self): operator = X * t_param + Z trotter_qrte = TrotterQrte() initial_state = Zero + evolution_problem = EvolutionProblem(operator, 1, initial_state, t_param=t_param) with assert_raises(ValueError): - _ = trotter_qrte.evolve(operator, 1, initial_state, t_param=t_param) + _ = trotter_qrte.evolve(evolution_problem) def test_trotter_qrte_trotter_binding_missing_param(self): """Test for trotter qrte with binding and missing param.""" @@ -139,8 +130,9 @@ def test_trotter_qrte_trotter_binding_missing_param(self): operator = X * t_param + Z trotter_qrte = TrotterQrte() initial_state = Zero + evolution_problem = EvolutionProblem(operator, 1, initial_state) with assert_raises(ValueError): - _ = trotter_qrte.evolve(operator, 1, initial_state) + _ = trotter_qrte.evolve(evolution_problem) if __name__ == "__main__": From 33d7d4c86b08474a6e9c5f7cd4335584e2826322 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 7 Mar 2022 13:50:39 +0100 Subject: [PATCH 067/145] Code refactoring. --- qiskit/algorithms/evolvers/evolution_problem.py | 2 +- qiskit/algorithms/evolvers/evolution_result.py | 2 +- qiskit/algorithms/evolvers/evolver.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index bb9ef7efcdad..c1fd920d8717 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -15,9 +15,9 @@ from typing import Union, Optional, Dict from qiskit import QuantumCircuit -from qiskit.algorithms.list_or_dict import ListOrDict from qiskit.circuit import Parameter from qiskit.opflow import OperatorBase, StateFn +from ..list_or_dict import ListOrDict class EvolutionProblem: diff --git a/qiskit/algorithms/evolvers/evolution_result.py b/qiskit/algorithms/evolvers/evolution_result.py index 444f03c7d8c9..6a4b4e91c3d3 100644 --- a/qiskit/algorithms/evolvers/evolution_result.py +++ b/qiskit/algorithms/evolvers/evolution_result.py @@ -15,7 +15,7 @@ from qiskit import QuantumCircuit from qiskit.algorithms.list_or_dict import ListOrDict from qiskit.opflow import StateFn -from qiskit.algorithms.algorithm_result import AlgorithmResult +from ..algorithm_result import AlgorithmResult class EvolutionResult(AlgorithmResult): diff --git a/qiskit/algorithms/evolvers/evolver.py b/qiskit/algorithms/evolvers/evolver.py index 101af197493e..15463c09097a 100644 --- a/qiskit/algorithms/evolvers/evolver.py +++ b/qiskit/algorithms/evolvers/evolver.py @@ -14,8 +14,8 @@ from abc import ABC, abstractmethod -from .evolution_problem import EvolutionProblem -from .evolution_result import EvolutionResult +from . import EvolutionProblem +from . import EvolutionResult class Evolver(ABC): From a7b711a386dd4227d2eb337cbcf1044e5a712169 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 7 Mar 2022 14:44:49 +0100 Subject: [PATCH 068/145] Code refactoring. --- .../imaginary}/__init__.py | 3 +-- .../evolvers/imaginary/imaginary_evolver.py | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) rename qiskit/algorithms/{quantum_time_evolution => evolvers/imaginary}/__init__.py (87%) create mode 100644 qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py diff --git a/qiskit/algorithms/quantum_time_evolution/__init__.py b/qiskit/algorithms/evolvers/imaginary/__init__.py similarity index 87% rename from qiskit/algorithms/quantum_time_evolution/__init__.py rename to qiskit/algorithms/evolvers/imaginary/__init__.py index 5fef55731f89..b3ac36d2a6d9 100644 --- a/qiskit/algorithms/quantum_time_evolution/__init__.py +++ b/qiskit/algorithms/evolvers/imaginary/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 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 @@ -9,4 +9,3 @@ # 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. -""" Quantum Time Evolution package """ diff --git a/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py b/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py new file mode 100644 index 000000000000..67c2d504574e --- /dev/null +++ b/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py @@ -0,0 +1,21 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021, 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. + +"""Base class for Quantum Imaginary Time Evolution used for typing purposes.""" + +from abc import ABC + +from .. import Evolver + + +class ImaginaryEvolver(Evolver, ABC): + """Base class for Quantum Imaginary Time Evolution used for typing purposes.""" From 94dc0d0356dde9041d171133ffe7755c1f0ec837 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 7 Mar 2022 14:53:28 +0100 Subject: [PATCH 069/145] Code refactoring. --- .../notes/time-evo-framework-9d58827bdbbebd62.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml b/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml index 208c7c372066..ba3af2234a8a 100644 --- a/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml +++ b/releasenotes/notes/time-evo-framework-9d58827bdbbebd62.yaml @@ -2,13 +2,13 @@ features: - | Interfaces for the unified framework for Quantum Time Evolution are introduced. - :class:`~qiskit.algorithms.time_evolution.EvolutionProblem` defines an evolution problem and + :class:`~qiskit.algorithms.evolvers.EvolutionProblem` defines an evolution problem and can be passed to any evolution algorithm available in the framework. - :class:`~qiskit.algorithms.time_evolution.Evolver` interface serves as a base for + :class:`~qiskit.algorithms.evolvers.Evolver` interface serves as a base for any time evolution algorithm for evolving quantum states, including evolutions based on time-dependent Hamiltonians. - :class:`~qiskit.algorithms.time_evolution.imaginary.ImaginaryEvolver` and - :class:`~qiskit.algorithms.time_evolution.real.RealEvolver` are derived interfaces for + :class:`~qiskit.algorithms.evolvers.imaginary.ImaginaryEvolver` and + :class:`~qiskit.algorithms.evolvers.real.RealEvolver` are derived interfaces for imaginary and real time evolution cases respectively. - :class:`~qiskit.algorithms.time_evolution.EvolutionResult` is introduced as a result object + :class:`~qiskit.algorithms.evolvers.EvolutionResult` is introduced as a result object for quantum time evolution algorithms. From b48d9fb33fba5e9616ec9a544b11cbcd62ca261e Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 8 Mar 2022 09:53:44 +0100 Subject: [PATCH 070/145] Fixed cyclic imports. --- qiskit/algorithms/__init__.py | 4 +++- qiskit/algorithms/evolvers/__init__.py | 6 ------ qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py | 2 +- qiskit/algorithms/evolvers/real/real_evolver.py | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index 77df82c3f159..5d864aa99fec 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -191,7 +191,9 @@ """ from .algorithm_result import AlgorithmResult -from .evolvers import EvolutionResult, EvolutionProblem, RealEvolver, ImaginaryEvolver +from .evolvers import EvolutionResult, EvolutionProblem +from .evolvers.real.real_evolver import RealEvolver +from .evolvers.imaginary.imaginary_evolver import ImaginaryEvolver from .variational_algorithm import VariationalAlgorithm, VariationalResult from .amplitude_amplifiers import Grover, GroverResult, AmplificationProblem from .amplitude_estimators import ( diff --git a/qiskit/algorithms/evolvers/__init__.py b/qiskit/algorithms/evolvers/__init__.py index 605d8e4c1c33..d4383fe9e05b 100644 --- a/qiskit/algorithms/evolvers/__init__.py +++ b/qiskit/algorithms/evolvers/__init__.py @@ -11,16 +11,10 @@ # that they have been altered from the originals. """ Quantum Time Evolution package """ -from .evolver import Evolver from .evolution_result import EvolutionResult -from .real.real_evolver import RealEvolver -from .imaginary.imaginary_evolver import ImaginaryEvolver from .evolution_problem import EvolutionProblem __all__ = [ - "Evolver", "EvolutionResult", - "RealEvolver", - "ImaginaryEvolver", "EvolutionProblem", ] diff --git a/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py b/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py index 67c2d504574e..4b1ab96eecdf 100644 --- a/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py +++ b/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py @@ -14,7 +14,7 @@ from abc import ABC -from .. import Evolver +from ..evolver import Evolver class ImaginaryEvolver(Evolver, ABC): diff --git a/qiskit/algorithms/evolvers/real/real_evolver.py b/qiskit/algorithms/evolvers/real/real_evolver.py index 5197b1d69d6a..c1663ed3868c 100644 --- a/qiskit/algorithms/evolvers/real/real_evolver.py +++ b/qiskit/algorithms/evolvers/real/real_evolver.py @@ -14,7 +14,7 @@ from abc import ABC -from .. import Evolver +from ..evolver import Evolver class RealEvolver(Evolver, ABC): From 5925403d0be9b7d03fbfc1c2796d45b9e8249688 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 8 Mar 2022 10:04:41 +0100 Subject: [PATCH 071/145] Fixed pylint. --- qiskit/algorithms/evolvers/evolver.py | 4 ++-- .../algorithms/evolvers/real/trotterization/trotter_qrte.py | 6 ++---- .../implementations/trotterization/test_trotter_qrte.py | 1 - 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolver.py b/qiskit/algorithms/evolvers/evolver.py index 15463c09097a..84e765623389 100644 --- a/qiskit/algorithms/evolvers/evolver.py +++ b/qiskit/algorithms/evolvers/evolver.py @@ -22,12 +22,12 @@ class Evolver(ABC): """Interface class for quantum time evolution.""" @abstractmethod - def evolve(self, evolver_problem: EvolutionProblem) -> EvolutionResult: + def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: """ Evolves an initial state in the evolution_problem according to a Hamiltonian provided. Args: - evolver_problem: EvolutionProblem instance that includes definition of an evolution + evolution_problem: EvolutionProblem instance that includes definition of an evolution problem. Returns: diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 4e271748eda2..69502dd8a8b0 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -14,10 +14,6 @@ from typing import Union, Dict -from ... import EvolutionProblem, EvolutionResult, RealEvolver -from trotter_ops_validator import ( - _is_op_bound, -) from qiskit.circuit import Parameter from qiskit.opflow import ( OperatorBase, @@ -27,6 +23,8 @@ ) from qiskit.circuit.library import PauliEvolutionGate from qiskit.synthesis import ProductFormula, LieTrotter +from .trotter_ops_validator import _is_op_bound +from ... import EvolutionProblem, EvolutionResult, RealEvolver class TrotterQrte(RealEvolver): diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 7105be0b4483..56ccf5a75d4a 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -32,7 +32,6 @@ Zero, VectorStateFn, StateFn, - MatrixOp, I, Y, ) From 2340448b879074fc93173c233b7e60702a4acb7a Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 8 Mar 2022 10:05:10 +0100 Subject: [PATCH 072/145] Name fix. --- qiskit/algorithms/evolvers/evolver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolver.py b/qiskit/algorithms/evolvers/evolver.py index 15463c09097a..84e765623389 100644 --- a/qiskit/algorithms/evolvers/evolver.py +++ b/qiskit/algorithms/evolvers/evolver.py @@ -22,12 +22,12 @@ class Evolver(ABC): """Interface class for quantum time evolution.""" @abstractmethod - def evolve(self, evolver_problem: EvolutionProblem) -> EvolutionResult: + def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: """ Evolves an initial state in the evolution_problem according to a Hamiltonian provided. Args: - evolver_problem: EvolutionProblem instance that includes definition of an evolution + evolution_problem: EvolutionProblem instance that includes definition of an evolution problem. Returns: From c8701a51b063831af7ea5f129a0e4199c94957cb Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 8 Mar 2022 10:08:08 +0100 Subject: [PATCH 073/145] Import fix. --- qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 69502dd8a8b0..b6be3223cde6 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -14,6 +14,7 @@ from typing import Union, Dict +from qiskit.algorithms import EvolutionProblem, EvolutionResult, RealEvolver from qiskit.circuit import Parameter from qiskit.opflow import ( OperatorBase, @@ -24,7 +25,6 @@ from qiskit.circuit.library import PauliEvolutionGate from qiskit.synthesis import ProductFormula, LieTrotter from .trotter_ops_validator import _is_op_bound -from ... import EvolutionProblem, EvolutionResult, RealEvolver class TrotterQrte(RealEvolver): From a3da72d89db16d1009c9a9cc1885d9f85b89985e Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 8 Mar 2022 10:45:17 +0100 Subject: [PATCH 074/145] Lint fix. --- .../real/implementations/trotterization/test_trotter_qrte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 56ccf5a75d4a..e305377932eb 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -14,6 +14,7 @@ import unittest +from test.python.opflow import QiskitOpflowTestCase from ddt import ddt, data import numpy as np from numpy.testing import assert_raises @@ -36,7 +37,6 @@ Y, ) from qiskit.synthesis import SuzukiTrotter, QDrift -from test.python.opflow import QiskitOpflowTestCase @ddt From d5114fb260e227203e5628c1021b5ae0a627e71f Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 8 Mar 2022 11:22:30 +0100 Subject: [PATCH 075/145] Lint fix. --- .../evolvers/real/trotterization/trotter_ops_validator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py index 4cc82524d024..9d37304cfacb 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py @@ -126,7 +126,7 @@ def _is_operator_parametrized(operator: PauliOp) -> bool: Boolean flag indicating whether the PauliOp is parametrized or not. """ return ( - not isinstance(operator.coeff, ParameterExpression) - and not isinstance(operator.coeff, Parameter) - or len(operator.coeff.parameters) == 0 + not isinstance(operator.coeff, Parameter) + if not isinstance(operator.coeff, ParameterExpression) + else len(operator.coeff.parameters) == 0 ) From 5b5004a444ab8d09501f5577afaa89f8d1656bae Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 8 Mar 2022 15:29:47 +0100 Subject: [PATCH 076/145] Code refactoring. --- .../real/trotterization/trotter_ops_validator.py | 6 +++--- .../evolvers/real/trotterization/trotter_qrte.py | 13 ++++++------- .../trotterization/test_trotter_ops_validator.py | 4 ++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py index 9d37304cfacb..253fbbc4cd5f 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py @@ -23,7 +23,7 @@ ) -def _is_op_bound(operator: Union[SummedOp, PauliOp, OperatorBase]) -> None: +def is_op_bound(operator: Union[SummedOp, PauliOp, OperatorBase]) -> None: """Checks if an operator provided has all parameters bound. Args: @@ -39,7 +39,7 @@ def _is_op_bound(operator: Union[SummedOp, PauliOp, OperatorBase]) -> None: ) -def _validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBase]): +def validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBase]): """Validates that a Hamiltonian is of a correct type and with expected dependence on parameters. @@ -78,7 +78,7 @@ def _is_pauli_lin_single_param(operator: PauliOp) -> bool: operator: Operator to be checked. Returns: - True or False depending on whether an operator is linear in a single param and only contains' + True or False depending on whether an operator is linear in a single param and only contains a single param. Raises: diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index b6be3223cde6..f32b89e93759 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -24,7 +24,7 @@ ) from qiskit.circuit.library import PauliEvolutionGate from qiskit.synthesis import ProductFormula, LieTrotter -from .trotter_ops_validator import _is_op_bound +from .trotter_ops_validator import is_op_bound class TrotterQrte(RealEvolver): @@ -53,7 +53,7 @@ def __init__(self, product_formula: ProductFormula = LieTrotter()) -> None: """ Args: product_formula: A Lie-Trotter-Suzuki product formula. The default is the Lie-Trotter - first order product formula with a single repetition. + first order product formula with a single repetition. """ self.product_formula = product_formula @@ -102,9 +102,8 @@ def _try_binding_params( Tries binding parameters in a Hamiltonian. Args: - hamiltonian: - The operator to evolve. Can also be provided as list of non-commuting - operators where the elements are sums of commuting operators. + hamiltonian: The Hamiltonian of that defines an evolution. Can also be provided as list + of non-commuting operators where the elements are sums of commuting operators. For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to certain values. @@ -124,7 +123,7 @@ def _try_binding_params( op_bound = op.bind_parameters(hamiltonian_value_dict) else: op_bound = op - _is_op_bound(op_bound) + is_op_bound(op_bound) op_list.append(op_bound) return sum(op_list) elif isinstance( @@ -135,7 +134,7 @@ def _try_binding_params( else: op_bound = hamiltonian - _is_op_bound(op_bound) + is_op_bound(op_bound) return op_bound else: raise ValueError( diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py index 126f26401298..5779893d0d02 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py @@ -19,7 +19,7 @@ import numpy as np from qiskit.algorithms.evolvers.real.trotterization.trotter_ops_validator import ( - _validate_hamiltonian_form, + validate_hamiltonian_form, _is_pauli_lin_single_param, ) from qiskit.circuit.library import EfficientSU2 @@ -50,7 +50,7 @@ def test_validate_hamiltonian_form(self, hamiltonian, expected): """Tests that a Hamiltonian is of a valid form supported by the TrotterQrte algorithm.""" valid = True try: - _validate_hamiltonian_form(hamiltonian) + validate_hamiltonian_form(hamiltonian) except ValueError: valid = False From c0b5d21b420cfd39a541509d8a4b6bb242517b9d Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 8 Mar 2022 16:33:51 +0100 Subject: [PATCH 077/145] Extracted and refactored aux_ops_evaluator.py --- qiskit/algorithms/aux_ops_evaluator.py | 130 ++++++++++++++++++ .../algorithms/minimum_eigen_solvers/vqe.py | 57 +------- 2 files changed, 133 insertions(+), 54 deletions(-) create mode 100644 qiskit/algorithms/aux_ops_evaluator.py diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py new file mode 100644 index 000000000000..af1004d8e8d2 --- /dev/null +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -0,0 +1,130 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. +from typing import Tuple, Union, List + +import numpy as np + +from qiskit.algorithms.minimum_eigen_solvers.minimum_eigen_solver import ListOrDict +from qiskit.opflow import ( + CircuitSampler, + ListOp, + StateFn, + CircuitStateFn, + OperatorBase, + ExpectationBase, +) +from qiskit.providers import BaseBackend, Backend +from qiskit.utils import QuantumInstance + + +def _eval_observabless( + quantum_instance: Union[QuantumInstance, BaseBackend, Backend], + ansatz: OperatorBase, + parameters: np.ndarray, + observables: ListOrDict[OperatorBase], + expectation: ExpectationBase, + threshold: float = 1e-12, +) -> ListOrDict[Tuple[complex, complex]]: + """ + Accepts a list or a dictionary of operators and calculates their expectation values - means + and standard deviations. They are calculated with respect to a parametrized ansatz provided. + An ansatz is bound using the list of parameter values provided. A user can optionally provide a + threshold value which filters mean values falling below the threshold. + + Args: + quantum_instance: A quantum instance used for calculations. + ansatz: A parametrized ansatz that expectation values are computed against. + parameters: An ordered list of parameters values used to bind an ansatz. They must follow an + order of parameters in an ansatz (ansatz.ordered_parameters). + observables: A list or a dictionary of operators whose expectation values are to be + calculated. + expectation: An instance of ExpectationBase which defines a method for calculating + expectation values. + threshold: A threshold value that defines which mean values should be neglected (helpful for + ignoring numerical instabilities close to 0). + + Returns: + A list or a dictionary of tuples (mean, standard deviation). + + """ + + # Create new CircuitSampler to avoid breaking existing one's caches. + sampler = CircuitSampler(quantum_instance) + + list_op = _prepare_list_op(observables) + + param_dict = dict(zip(ansatz.ordered_parameters, parameters)) + + bound_ansatz = ansatz.bind_parameters(param_dict) + + observables_expect = expectation.convert( + StateFn(list_op, is_measurement=True).compose(CircuitStateFn(bound_ansatz)) + ) + observables_expect_sampled = sampler.convert(observables_expect) + + # compute means + values = np.real(observables_expect_sampled.eval()) + + # compute standard deviations + std_devs = _compute_std_devs( + observables_expect_sampled, observables, expectation, quantum_instance + ) + + # Discard values below threshold + observables_means = values * (np.abs(values) > threshold) + # zip means and standard deviations into tuples + observables_results = zip(observables_means, std_devs) + + # Return None eigenvalues for None operators if observables is a list. + # None operators are already dropped in compute_minimum_eigenvalue if observables is a dict. + observables_eigenvalues = _prepare_result(observables_results, observables) + + return observables_eigenvalues + + +def _prepare_list_op(observables: ListOrDict[OperatorBase]) -> ListOp[OperatorBase]: + if isinstance(observables, dict): + list_op = ListOp(list(observables.values())) + else: + list_op = ListOp(observables) + return list_op + + +def _prepare_result( + observables_results, + observables: ListOrDict[OperatorBase], +) -> ListOrDict[Tuple[complex, complex]]: + if isinstance(observables, list): + observables_eigenvalues = [None] * len(observables) + key_value_iterator = enumerate(observables_results) + else: + observables_eigenvalues = {} + key_value_iterator = zip(observables.keys(), observables_results) + for key, value in key_value_iterator: + if observables[key] is not None: + observables_eigenvalues[key] = value + return observables_eigenvalues + + +def _compute_std_devs( + observables_expect_sampled, + observables: ListOrDict[OperatorBase], + expectation: ExpectationBase, + quantum_instance: Union[QuantumInstance, BaseBackend, Backend], +) -> List[complex]: + variances = np.real(expectation.compute_variance(observables_expect_sampled)) + if not isinstance(variances, np.ndarray) and variances == 0.0: + # when `variances` is a single value equal to 0., our expectation value is exact and we + # manually ensure the variances to be a list of the correct length + variances = np.zeros(len(observables), dtype=float) + std_devs = np.sqrt(variances / quantum_instance.run_config.shots) + return std_devs diff --git a/qiskit/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/algorithms/minimum_eigen_solvers/vqe.py index b87d481f80a2..94462896a46e 100755 --- a/qiskit/algorithms/minimum_eigen_solvers/vqe.py +++ b/qiskit/algorithms/minimum_eigen_solvers/vqe.py @@ -39,6 +39,7 @@ from qiskit.utils.validation import validate_min from qiskit.utils.backend_utils import is_aer_provider from qiskit.utils import QuantumInstance, algorithm_globals +from ..aux_ops_evaluator import _eval_aux_ops from ..optimizers import Optimizer, SLSQP from ..variational_algorithm import VariationalAlgorithm, VariationalResult from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult, ListOrDict @@ -422,59 +423,6 @@ def extract_circuits(op): def supports_aux_operators(cls) -> bool: return True - def _eval_aux_ops( - self, - parameters: np.ndarray, - aux_operators: ListOrDict[OperatorBase], - expectation: ExpectationBase, - threshold: float = 1e-12, - ) -> ListOrDict[Tuple[complex, complex]]: - # Create new CircuitSampler to avoid breaking existing one's caches. - sampler = CircuitSampler(self.quantum_instance) - - if isinstance(aux_operators, dict): - list_op = ListOp(list(aux_operators.values())) - else: - list_op = ListOp(aux_operators) - - aux_op_expect = expectation.convert( - StateFn(list_op, is_measurement=True).compose( - CircuitStateFn(self.ansatz.bind_parameters(parameters)) - ) - ) - aux_op_expect_sampled = sampler.convert(aux_op_expect) - - # compute means - values = np.real(aux_op_expect_sampled.eval()) - - # compute standard deviations - variances = np.real(expectation.compute_variance(aux_op_expect_sampled)) - if not isinstance(variances, np.ndarray) and variances == 0.0: - # when `variances` is a single value equal to 0., our expectation value is exact and we - # manually ensure the variances to be a list of the correct length - variances = np.zeros(len(aux_operators), dtype=float) - std_devs = np.sqrt(variances / self.quantum_instance.run_config.shots) - - # Discard values below threshold - aux_op_means = values * (np.abs(values) > threshold) - # zip means and standard deviations into tuples - aux_op_results = zip(aux_op_means, std_devs) - - # 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[ListOrDict[OperatorBase]] = None ) -> MinimumEigensolverResult: @@ -576,7 +524,8 @@ def compute_minimum_eigenvalue( self._ret = result if aux_operators is not None: - aux_values = self._eval_aux_ops(opt_result.x, aux_operators, expectation=expectation) + aux_values = _eval_aux_ops(self.quantum_instance, self.ansatz, opt_result.x, + aux_operators, expectation=expectation) result.aux_operator_eigenvalues = aux_values return result From e5ddb44f30fd165af6b33819455c2c4699281148 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 8 Mar 2022 16:36:25 +0100 Subject: [PATCH 078/145] Code refactoring --- qiskit/algorithms/aux_ops_evaluator.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py index af1004d8e8d2..c29bb30817f1 100644 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 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 @@ -86,17 +86,15 @@ def _eval_observabless( # Return None eigenvalues for None operators if observables is a list. # None operators are already dropped in compute_minimum_eigenvalue if observables is a dict. - observables_eigenvalues = _prepare_result(observables_results, observables) - return observables_eigenvalues + return _prepare_result(observables_results, observables) def _prepare_list_op(observables: ListOrDict[OperatorBase]) -> ListOp[OperatorBase]: if isinstance(observables, dict): - list_op = ListOp(list(observables.values())) - else: - list_op = ListOp(observables) - return list_op + return ListOp(list(observables.values())) + + return ListOp(observables) def _prepare_result( From 978c4f3b4d6f972df72c72874def88a5d056c6ce Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 9 Mar 2022 10:47:09 +0100 Subject: [PATCH 079/145] Implemented unit test. --- qiskit/algorithms/aux_ops_evaluator.py | 6 +- .../algorithms/minimum_eigen_solvers/vqe.py | 4 +- .../algorithms/test_aux_ops_evaluator.py | 60 +++++++++++++++++++ 3 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 test/python/algorithms/test_aux_ops_evaluator.py diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py index c29bb30817f1..a03a2dbb23cb 100644 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -9,6 +9,8 @@ # 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. +"""Evaluator of auxiliary operators for algorithms.""" + from typing import Tuple, Union, List import numpy as np @@ -26,7 +28,7 @@ from qiskit.utils import QuantumInstance -def _eval_observabless( +def eval_observables( quantum_instance: Union[QuantumInstance, BaseBackend, Backend], ansatz: OperatorBase, parameters: np.ndarray, @@ -90,7 +92,7 @@ def _eval_observabless( return _prepare_result(observables_results, observables) -def _prepare_list_op(observables: ListOrDict[OperatorBase]) -> ListOp[OperatorBase]: +def _prepare_list_op(observables: ListOrDict[OperatorBase]) -> ListOp: if isinstance(observables, dict): return ListOp(list(observables.values())) diff --git a/qiskit/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/algorithms/minimum_eigen_solvers/vqe.py index 94462896a46e..c33f3b7e137b 100755 --- a/qiskit/algorithms/minimum_eigen_solvers/vqe.py +++ b/qiskit/algorithms/minimum_eigen_solvers/vqe.py @@ -39,7 +39,7 @@ from qiskit.utils.validation import validate_min from qiskit.utils.backend_utils import is_aer_provider from qiskit.utils import QuantumInstance, algorithm_globals -from ..aux_ops_evaluator import _eval_aux_ops +from ..aux_ops_evaluator import eval_observables from ..optimizers import Optimizer, SLSQP from ..variational_algorithm import VariationalAlgorithm, VariationalResult from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult, ListOrDict @@ -524,7 +524,7 @@ def compute_minimum_eigenvalue( self._ret = result if aux_operators is not None: - aux_values = _eval_aux_ops(self.quantum_instance, self.ansatz, opt_result.x, + aux_values = eval_observables(self.quantum_instance, self.ansatz, opt_result.x, aux_operators, expectation=expectation) result.aux_operator_eigenvalues = aux_values diff --git a/test/python/algorithms/test_aux_ops_evaluator.py b/test/python/algorithms/test_aux_ops_evaluator.py new file mode 100644 index 000000000000..cfdb0925b3bc --- /dev/null +++ b/test/python/algorithms/test_aux_ops_evaluator.py @@ -0,0 +1,60 @@ +# 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. +"""Tests evaluator of auxiliary operators for algorithms.""" + +import unittest +from test.python.algorithms import QiskitAlgorithmsTestCase + +import numpy as np +from ddt import ddt + +from qiskit import BasicAer +from qiskit.algorithms.aux_ops_evaluator import eval_observables +from qiskit.circuit.library import EfficientSU2 +from qiskit.opflow import PauliExpectation, PauliSumOp +from qiskit.utils import QuantumInstance + + +@ddt +class TestAuxOpsEvaluator(QiskitAlgorithmsTestCase): + """Tests evaluator of auxiliary operators for algorithms.""" + + def test_eval_observables(self): + """Tests evaluator of auxiliary operators for algorithms.""" + + quantum_instance = QuantumInstance( + BasicAer.get_backend("qasm_simulator"), + shots=1, + seed_simulator=7, + seed_transpiler=7, + ) + ansatz = EfficientSU2(1) + parameters = np.array([1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0], dtype=float) + expectation = PauliExpectation() + threshold = 1e-8 + + observables = [ + PauliSumOp.from_list([("II", 2.0)]), + PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]), + ] + + result = eval_observables( + quantum_instance, ansatz, parameters, observables, expectation, threshold + ) + + expected_result = [(2.0, 0.0), (0.0, 0.0)] + + np.testing.assert_array_almost_equal(result, expected_result) + + +if __name__ == "__main__": + unittest.main() From 1beb615bd04b526681c91432a9d3f53e2eb6f120 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 9 Mar 2022 14:20:53 +0100 Subject: [PATCH 080/145] Extended unit test. --- .../algorithms/test_aux_ops_evaluator.py | 96 +++++++++++++++---- test/python/algorithms/test_vqe.py | 2 +- 2 files changed, 79 insertions(+), 19 deletions(-) diff --git a/test/python/algorithms/test_aux_ops_evaluator.py b/test/python/algorithms/test_aux_ops_evaluator.py index cfdb0925b3bc..45d8bb9c03c0 100644 --- a/test/python/algorithms/test_aux_ops_evaluator.py +++ b/test/python/algorithms/test_aux_ops_evaluator.py @@ -12,46 +12,106 @@ """Tests evaluator of auxiliary operators for algorithms.""" import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase +from test.python.algorithms import QiskitAlgorithmsTestCase import numpy as np -from ddt import ddt +from ddt import ddt, data, unpack from qiskit import BasicAer from qiskit.algorithms.aux_ops_evaluator import eval_observables from qiskit.circuit.library import EfficientSU2 -from qiskit.opflow import PauliExpectation, PauliSumOp -from qiskit.utils import QuantumInstance +from qiskit.opflow import PauliSumOp, X, Z, I, ExpectationFactory +from qiskit.utils import QuantumInstance, algorithm_globals @ddt class TestAuxOpsEvaluator(QiskitAlgorithmsTestCase): """Tests evaluator of auxiliary operators for algorithms.""" - def test_eval_observables(self): + def setUp(self): + super().setUp() + self.seed = 50 + algorithm_globals.random_seed = self.seed + self.h2_op = ( + -1.052373245772859 * (I ^ I) + + 0.39793742484318045 * (I ^ Z) + - 0.39793742484318045 * (Z ^ I) + - 0.01128010425623538 * (Z ^ Z) + + 0.18093119978423156 * (X ^ X) + ) + + self.threshold = 1e-8 + + @data( + ( + [ + PauliSumOp.from_list([("II", 2.0)]), + PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]), + ], + [(1.9999999999999998, 0.0), (0.3819044157958812, 0.0)], + ), + ( + [ + PauliSumOp.from_list([("ZZ", 2.0)]), + ], + [(-0.4723823368164749, 0.0)], + ), + ) + @unpack + def test_eval_observables_statevector(self, observables, expected_result): """Tests evaluator of auxiliary operators for algorithms.""" + backend = BasicAer.get_backend("statevector_simulator") quantum_instance = QuantumInstance( - BasicAer.get_backend("qasm_simulator"), - shots=1, - seed_simulator=7, - seed_transpiler=7, + backend=backend, shots=1, seed_simulator=self.seed, seed_transpiler=self.seed ) + ansatz = EfficientSU2(1) parameters = np.array([1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0], dtype=float) - expectation = PauliExpectation() - threshold = 1e-8 - - observables = [ - PauliSumOp.from_list([("II", 2.0)]), - PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]), - ] + expectation = ExpectationFactory.build( + operator=self.h2_op, + backend=quantum_instance, + ) result = eval_observables( - quantum_instance, ansatz, parameters, observables, expectation, threshold + quantum_instance, ansatz, parameters, observables, expectation, self.threshold + ) + + np.testing.assert_array_almost_equal(result, expected_result) + + @data( + ( + [ + PauliSumOp.from_list([("II", 2.0)]), + PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]), + ], + [(2.0, 0.0), (0.39355468750000006, 0.015266813075786104)], + ), + ( + [ + PauliSumOp.from_list([("ZZ", 2.0)]), + ], + [(-0.42578124999999967, 0.06106725230314441)], + ), + ) + @unpack + def test_eval_observables_qasm(self, observables, expected_result): + """Tests evaluator of auxiliary operators for algorithms.""" + backend = BasicAer.get_backend("qasm_simulator") + quantum_instance = QuantumInstance( + backend=backend, shots=1024, seed_simulator=self.seed, seed_transpiler=self.seed + ) + + ansatz = EfficientSU2(1) + parameters = np.array([1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0], dtype=float) + expectation = ExpectationFactory.build( + operator=self.h2_op, + backend=quantum_instance, ) - expected_result = [(2.0, 0.0), (0.0, 0.0)] + result = eval_observables( + quantum_instance, ansatz, parameters, observables, expectation, self.threshold + ) np.testing.assert_array_almost_equal(result, expected_result) diff --git a/test/python/algorithms/test_vqe.py b/test/python/algorithms/test_vqe.py index 6400f6a07a66..731238bd4e6d 100644 --- a/test/python/algorithms/test_vqe.py +++ b/test/python/algorithms/test_vqe.py @@ -50,7 +50,7 @@ from qiskit.transpiler import PassManager, PassManagerConfig from qiskit.transpiler.preset_passmanagers import level_1_pass_manager from qiskit.utils import QuantumInstance, algorithm_globals, has_aer -from ..transpiler._dummy_passes import DummyAP +from test.python.transpiler._dummy_passes import DummyAP if has_aer(): from qiskit import Aer From 4c763ea859ebeb399b934a6eebf1d39482104a1f Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 9 Mar 2022 14:32:32 +0100 Subject: [PATCH 081/145] Date fixed. --- qiskit/algorithms/minimum_eigen_solvers/vqe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/algorithms/minimum_eigen_solvers/vqe.py index c33f3b7e137b..35f447090f97 100755 --- a/qiskit/algorithms/minimum_eigen_solvers/vqe.py +++ b/qiskit/algorithms/minimum_eigen_solvers/vqe.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (C) Copyright IBM 2018, 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 From da4cea870c87ad0475edd45e5c423e6301470f47 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 9 Mar 2022 15:31:05 +0100 Subject: [PATCH 082/145] Reno added. --- .../notes/refactor-aux-operators-79d790f8a693a7c0.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml diff --git a/releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml b/releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml new file mode 100644 index 000000000000..679d24b4c867 --- /dev/null +++ b/releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml @@ -0,0 +1,7 @@ +--- +other: + - | + :func:`qiskit.algorithms.aux_ops_evaluator.eval_observables` is added that originates from + a method previously residing in :class:`qiskit.algorithms.minimum_eigen_solvers.VQE`. The + method is general enough so that it can be used in other algorithms, for example time evolution + algorithms. The method is also refactored to be more modular. From 6d0fa654dc9bff2b561d2b3f53e9c729c428b32d Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 10 Mar 2022 09:22:03 +0100 Subject: [PATCH 083/145] Switched to bound ansatz. --- qiskit/algorithms/aux_ops_evaluator.py | 20 ++++++------------- .../algorithms/minimum_eigen_solvers/vqe.py | 8 ++++++-- .../algorithms/test_aux_ops_evaluator.py | 10 ++++++++-- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py index a03a2dbb23cb..cd91931af361 100644 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -15,6 +15,7 @@ import numpy as np +from qiskit import QuantumCircuit from qiskit.algorithms.minimum_eigen_solvers.minimum_eigen_solver import ListOrDict from qiskit.opflow import ( CircuitSampler, @@ -30,23 +31,19 @@ def eval_observables( quantum_instance: Union[QuantumInstance, BaseBackend, Backend], - ansatz: OperatorBase, - parameters: np.ndarray, + ansatz: QuantumCircuit, observables: ListOrDict[OperatorBase], expectation: ExpectationBase, threshold: float = 1e-12, ) -> ListOrDict[Tuple[complex, complex]]: """ Accepts a list or a dictionary of operators and calculates their expectation values - means - and standard deviations. They are calculated with respect to a parametrized ansatz provided. - An ansatz is bound using the list of parameter values provided. A user can optionally provide a - threshold value which filters mean values falling below the threshold. + and standard deviations. They are calculated with respect to an ansatz provided. A user can + optionally provide a threshold value which filters mean values falling below the threshold. Args: quantum_instance: A quantum instance used for calculations. - ansatz: A parametrized ansatz that expectation values are computed against. - parameters: An ordered list of parameters values used to bind an ansatz. They must follow an - order of parameters in an ansatz (ansatz.ordered_parameters). + ansatz: An unparametrized ansatz that expectation values are computed against. observables: A list or a dictionary of operators whose expectation values are to be calculated. expectation: An instance of ExpectationBase which defines a method for calculating @@ -56,7 +53,6 @@ def eval_observables( Returns: A list or a dictionary of tuples (mean, standard deviation). - """ # Create new CircuitSampler to avoid breaking existing one's caches. @@ -64,12 +60,8 @@ def eval_observables( list_op = _prepare_list_op(observables) - param_dict = dict(zip(ansatz.ordered_parameters, parameters)) - - bound_ansatz = ansatz.bind_parameters(param_dict) - observables_expect = expectation.convert( - StateFn(list_op, is_measurement=True).compose(CircuitStateFn(bound_ansatz)) + StateFn(list_op, is_measurement=True).compose(CircuitStateFn(ansatz)) ) observables_expect_sampled = sampler.convert(observables_expect) diff --git a/qiskit/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/algorithms/minimum_eigen_solvers/vqe.py index 35f447090f97..902026003ec6 100755 --- a/qiskit/algorithms/minimum_eigen_solvers/vqe.py +++ b/qiskit/algorithms/minimum_eigen_solvers/vqe.py @@ -524,8 +524,12 @@ def compute_minimum_eigenvalue( self._ret = result if aux_operators is not None: - aux_values = eval_observables(self.quantum_instance, self.ansatz, opt_result.x, - aux_operators, expectation=expectation) + param_dict = dict(zip(self.ansatz.ordered_parameters, opt_result.x)) + bound_ansatz = self.ansatz.bind_parameters(param_dict) + + aux_values = eval_observables( + self.quantum_instance, bound_ansatz, aux_operators, expectation=expectation + ) result.aux_operator_eigenvalues = aux_values return result diff --git a/test/python/algorithms/test_aux_ops_evaluator.py b/test/python/algorithms/test_aux_ops_evaluator.py index 45d8bb9c03c0..5c2bbaa542d4 100644 --- a/test/python/algorithms/test_aux_ops_evaluator.py +++ b/test/python/algorithms/test_aux_ops_evaluator.py @@ -72,9 +72,12 @@ def test_eval_observables_statevector(self, observables, expected_result): operator=self.h2_op, backend=quantum_instance, ) + param_dict = dict(zip(ansatz.ordered_parameters, parameters)) + + bound_ansatz = ansatz.bind_parameters(param_dict) result = eval_observables( - quantum_instance, ansatz, parameters, observables, expectation, self.threshold + quantum_instance, bound_ansatz, observables, expectation, self.threshold ) np.testing.assert_array_almost_equal(result, expected_result) @@ -108,9 +111,12 @@ def test_eval_observables_qasm(self, observables, expected_result): operator=self.h2_op, backend=quantum_instance, ) + param_dict = dict(zip(ansatz.ordered_parameters, parameters)) + + bound_ansatz = ansatz.bind_parameters(param_dict) result = eval_observables( - quantum_instance, ansatz, parameters, observables, expectation, self.threshold + quantum_instance, bound_ansatz, observables, expectation, self.threshold ) np.testing.assert_array_almost_equal(result, expected_result) From dec7733f9424d81b0b72599b62cadb1676996dd7 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 10 Mar 2022 09:58:02 +0100 Subject: [PATCH 084/145] Fix reno. --- .../notes/refactor-aux-operators-79d790f8a693a7c0.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml b/releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml index 679d24b4c867..d6814bb70f3c 100644 --- a/releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml +++ b/releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml @@ -2,6 +2,6 @@ other: - | :func:`qiskit.algorithms.aux_ops_evaluator.eval_observables` is added that originates from - a method previously residing in :class:`qiskit.algorithms.minimum_eigen_solvers.VQE`. The - method is general enough so that it can be used in other algorithms, for example time evolution - algorithms. The method is also refactored to be more modular. + a method previously residing in :class:`qiskit.algorithms.VQE`. The method is general enough so + that it can be used in other algorithms, for example time evolution algorithms. The method is + also refactored to be more modular. From d207daa13b0525db6880add1dae2b702affb7295 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 10 Mar 2022 12:55:35 +0100 Subject: [PATCH 085/145] Added docs. --- qiskit/algorithms/aux_ops_evaluator.py | 39 ++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py index cd91931af361..caa03f2d1ccf 100644 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -85,6 +85,15 @@ def eval_observables( def _prepare_list_op(observables: ListOrDict[OperatorBase]) -> ListOp: + """ + Accepts a list or a dictionary of operators and converts them to a ``ListOp``. + + Args: + observables: A list or a dictionary of operators. + + Returns: + A ``ListOp`` that includes all provided observables. + """ if isinstance(observables, dict): return ListOp(list(observables.values())) @@ -92,9 +101,21 @@ def _prepare_list_op(observables: ListOrDict[OperatorBase]) -> ListOp: def _prepare_result( - observables_results, + observables_results: List[Tuple[complex, complex]], observables: ListOrDict[OperatorBase], ) -> ListOrDict[Tuple[complex, complex]]: + """ + Prepares a list or a dictionary of eigenvalues from ``observables_results`` and + ``observables``. + + Args: + observables_results: A list of of tuples (mean, standard deviation). + observables: A list or a dictionary of operators whose expectation values are to be + calculated. + + Returns: + A list or a dictionary of tuples (mean, standard deviation). + """ if isinstance(observables, list): observables_eigenvalues = [None] * len(observables) key_value_iterator = enumerate(observables_results) @@ -108,11 +129,25 @@ def _prepare_result( def _compute_std_devs( - observables_expect_sampled, + observables_expect_sampled: OperatorBase, observables: ListOrDict[OperatorBase], expectation: ExpectationBase, quantum_instance: Union[QuantumInstance, BaseBackend, Backend], ) -> List[complex]: + """ + Calculates a list of standard deviations from expectation values of observables provided. + + Args: + observables_expect_sampled: Expected values of observables. + observables: A list or a dictionary of operators whose expectation values are to be + calculated. + expectation: An instance of ExpectationBase which defines a method for calculating + expectation values. + quantum_instance: A quantum instance used for calculations. + + Returns: + A list of standard deviations. + """ variances = np.real(expectation.compute_variance(observables_expect_sampled)) if not isinstance(variances, np.ndarray) and variances == 0.0: # when `variances` is a single value equal to 0., our expectation value is exact and we From 11be9feed0a153166d5ff78869734fe719d308c3 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 10 Mar 2022 13:28:17 +0100 Subject: [PATCH 086/145] Refactored unit test. --- .../algorithms/test_aux_ops_evaluator.py | 77 ++++++------------- 1 file changed, 23 insertions(+), 54 deletions(-) diff --git a/test/python/algorithms/test_aux_ops_evaluator.py b/test/python/algorithms/test_aux_ops_evaluator.py index 5c2bbaa542d4..924ecf42214e 100644 --- a/test/python/algorithms/test_aux_ops_evaluator.py +++ b/test/python/algorithms/test_aux_ops_evaluator.py @@ -41,6 +41,7 @@ def setUp(self): ) self.threshold = 1e-8 + self.backend_names = ["statevector_simulator", "qasm_simulator"] @data( ( @@ -61,65 +62,33 @@ def setUp(self): def test_eval_observables_statevector(self, observables, expected_result): """Tests evaluator of auxiliary operators for algorithms.""" - backend = BasicAer.get_backend("statevector_simulator") - quantum_instance = QuantumInstance( - backend=backend, shots=1, seed_simulator=self.seed, seed_transpiler=self.seed - ) - ansatz = EfficientSU2(1) parameters = np.array([1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0], dtype=float) - expectation = ExpectationFactory.build( - operator=self.h2_op, - backend=quantum_instance, - ) param_dict = dict(zip(ansatz.ordered_parameters, parameters)) - bound_ansatz = ansatz.bind_parameters(param_dict) - result = eval_observables( - quantum_instance, bound_ansatz, observables, expectation, self.threshold - ) - - np.testing.assert_array_almost_equal(result, expected_result) - - @data( - ( - [ - PauliSumOp.from_list([("II", 2.0)]), - PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]), - ], - [(2.0, 0.0), (0.39355468750000006, 0.015266813075786104)], - ), - ( - [ - PauliSumOp.from_list([("ZZ", 2.0)]), - ], - [(-0.42578124999999967, 0.06106725230314441)], - ), - ) - @unpack - def test_eval_observables_qasm(self, observables, expected_result): - """Tests evaluator of auxiliary operators for algorithms.""" - backend = BasicAer.get_backend("qasm_simulator") - quantum_instance = QuantumInstance( - backend=backend, shots=1024, seed_simulator=self.seed, seed_transpiler=self.seed - ) - - ansatz = EfficientSU2(1) - parameters = np.array([1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0], dtype=float) - expectation = ExpectationFactory.build( - operator=self.h2_op, - backend=quantum_instance, - ) - param_dict = dict(zip(ansatz.ordered_parameters, parameters)) - - bound_ansatz = ansatz.bind_parameters(param_dict) - - result = eval_observables( - quantum_instance, bound_ansatz, observables, expectation, self.threshold - ) - - np.testing.assert_array_almost_equal(result, expected_result) + for backend_name in self.backend_names: + shots = 2048 if backend_name == "qasm_simulator" else 1 + decimal = ( + 1 if backend_name == "qasm_simulator" else 6 + ) # to accommodate for qasm being imperfect + with self.subTest(msg=f"Test {backend_name} backend."): + backend = BasicAer.get_backend(backend_name) + quantum_instance = QuantumInstance( + backend=backend, + shots=shots, + seed_simulator=self.seed, + seed_transpiler=self.seed, + ) + expectation = ExpectationFactory.build( + operator=self.h2_op, + backend=quantum_instance, + ) + result = eval_observables( + quantum_instance, bound_ansatz, observables, expectation, self.threshold + ) + + np.testing.assert_array_almost_equal(result, expected_result, decimal=decimal) if __name__ == "__main__": From b8f36f8329253569a23ee0d279056b332ae64be5 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 10 Mar 2022 14:06:02 +0100 Subject: [PATCH 087/145] Lint fixed. --- test/python/algorithms/test_vqe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/algorithms/test_vqe.py b/test/python/algorithms/test_vqe.py index 731238bd4e6d..397a49d9a1ce 100644 --- a/test/python/algorithms/test_vqe.py +++ b/test/python/algorithms/test_vqe.py @@ -15,6 +15,7 @@ import logging import unittest from test.python.algorithms import QiskitAlgorithmsTestCase +from test.python.transpiler._dummy_passes import DummyAP from functools import partial import numpy as np @@ -50,7 +51,6 @@ from qiskit.transpiler import PassManager, PassManagerConfig from qiskit.transpiler.preset_passmanagers import level_1_pass_manager from qiskit.utils import QuantumInstance, algorithm_globals, has_aer -from test.python.transpiler._dummy_passes import DummyAP if has_aer(): from qiskit import Aer From 59e178556aee21e246d0531e5086ffe435159e3b Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 10 Mar 2022 15:38:15 +0100 Subject: [PATCH 088/145] Code review edits. --- qiskit/algorithms/aux_ops_evaluator.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py index caa03f2d1ccf..931ac2c91241 100644 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -31,19 +31,20 @@ def eval_observables( quantum_instance: Union[QuantumInstance, BaseBackend, Backend], - ansatz: QuantumCircuit, + quantum_state: QuantumCircuit, observables: ListOrDict[OperatorBase], expectation: ExpectationBase, threshold: float = 1e-12, ) -> ListOrDict[Tuple[complex, complex]]: """ Accepts a list or a dictionary of operators and calculates their expectation values - means - and standard deviations. They are calculated with respect to an ansatz provided. A user can - optionally provide a threshold value which filters mean values falling below the threshold. + and standard deviations. They are calculated with respect to a quantum state provided. A user + can optionally provide a threshold value which filters mean values falling below the threshold. Args: quantum_instance: A quantum instance used for calculations. - ansatz: An unparametrized ansatz that expectation values are computed against. + quantum_state: An unparametrized quantum circuit representing a quantum state that + expectation values are computed against. observables: A list or a dictionary of operators whose expectation values are to be calculated. expectation: An instance of ExpectationBase which defines a method for calculating @@ -53,15 +54,24 @@ def eval_observables( Returns: A list or a dictionary of tuples (mean, standard deviation). + + Raises: + ValueError: If a ``quantum_state`` with free parameters is provided. """ + if len(quantum_state.parameters) > 0: + raise ValueError( + "A parametrized quantum circuit representing a quantum_state was provided. It is not " + "allowed - the circuit cannot have free parameters." + ) + # Create new CircuitSampler to avoid breaking existing one's caches. sampler = CircuitSampler(quantum_instance) list_op = _prepare_list_op(observables) observables_expect = expectation.convert( - StateFn(list_op, is_measurement=True).compose(CircuitStateFn(ansatz)) + StateFn(list_op, is_measurement=True).compose(CircuitStateFn(quantum_state)) ) observables_expect_sampled = sampler.convert(observables_expect) From 853af9e295dc4b4d78c5d644c5c85e7ca56fa00c Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 10 Mar 2022 16:26:14 +0100 Subject: [PATCH 089/145] Added unit test cases for dicts. --- .../algorithms/test_aux_ops_evaluator.py | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/test/python/algorithms/test_aux_ops_evaluator.py b/test/python/algorithms/test_aux_ops_evaluator.py index 924ecf42214e..7491cedfac8e 100644 --- a/test/python/algorithms/test_aux_ops_evaluator.py +++ b/test/python/algorithms/test_aux_ops_evaluator.py @@ -17,8 +17,8 @@ import numpy as np from ddt import ddt, data, unpack -from qiskit import BasicAer from qiskit.algorithms.aux_ops_evaluator import eval_observables +from qiskit import BasicAer from qiskit.circuit.library import EfficientSU2 from qiskit.opflow import PauliSumOp, X, Z, I, ExpectationFactory from qiskit.utils import QuantumInstance, algorithm_globals @@ -57,6 +57,19 @@ def setUp(self): ], [(-0.4723823368164749, 0.0)], ), + ( + { + "op1": PauliSumOp.from_list([("II", 2.0)]), + "op2": PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]), + }, + {"op1": (1.9999999999999998, 0.0), "op2": (0.3819044157958812, 0.0)}, + ), + ( + { + "op1": PauliSumOp.from_list([("ZZ", 2.0)]), + }, + {"op1": (-0.4723823368164749, 0.0)}, + ), ) @unpack def test_eval_observables_statevector(self, observables, expected_result): @@ -88,7 +101,13 @@ def test_eval_observables_statevector(self, observables, expected_result): quantum_instance, bound_ansatz, observables, expectation, self.threshold ) - np.testing.assert_array_almost_equal(result, expected_result, decimal=decimal) + if isinstance(result, dict): + np.testing.assert_equal(list(result.keys()), list(expected_result.keys())) + np.testing.assert_array_almost_equal( + list(result.values()), list(expected_result.values()), decimal=decimal + ) + else: + np.testing.assert_array_almost_equal(result, expected_result, decimal=decimal) if __name__ == "__main__": From fb82de6adf592c9be90c7f870898d56b56e353cb Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 10 Mar 2022 18:12:58 +0100 Subject: [PATCH 090/145] Fixed reno reference. --- qiskit/algorithms/__init__.py | 14 ++++++++++++++ qiskit/algorithms/minimum_eigen_solvers/vqe.py | 2 +- .../refactor-aux-operators-79d790f8a693a7c0.yaml | 8 ++++---- test/python/algorithms/test_aux_ops_evaluator.py | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index d20c4f2e42cc..0c3ed197f2c6 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -182,6 +182,7 @@ PhaseEstimationResult IterativePhaseEstimation + Exceptions ========== @@ -189,6 +190,17 @@ :toctree: ../stubs/ AlgorithmError + + +Utility methods +--------------- + +Utility methods used by algorithms. + +.. autosummary:: + :toctree: ../stubs/ + + eval_observables """ from .algorithm_result import AlgorithmResult @@ -230,6 +242,7 @@ IterativePhaseEstimation, ) from .exceptions import AlgorithmError +from .aux_ops_evaluator import eval_observables __all__ = [ "AlgorithmResult", @@ -276,4 +289,5 @@ "PhaseEstimationResult", "IterativePhaseEstimation", "AlgorithmError", + "eval_observables", ] diff --git a/qiskit/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/algorithms/minimum_eigen_solvers/vqe.py index 34c4e041b62c..f96fafcb61c1 100755 --- a/qiskit/algorithms/minimum_eigen_solvers/vqe.py +++ b/qiskit/algorithms/minimum_eigen_solvers/vqe.py @@ -39,12 +39,12 @@ from qiskit.utils.validation import validate_min from qiskit.utils.backend_utils import is_aer_provider from qiskit.utils import QuantumInstance, algorithm_globals -from ..aux_ops_evaluator import eval_observables from ..list_or_dict import ListOrDict from ..optimizers import Optimizer, SLSQP from ..variational_algorithm import VariationalAlgorithm, VariationalResult from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult from ..exceptions import AlgorithmError +from ..aux_ops_evaluator import eval_observables logger = logging.getLogger(__name__) diff --git a/releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml b/releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml index d6814bb70f3c..4ff62389914d 100644 --- a/releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml +++ b/releasenotes/notes/refactor-aux-operators-79d790f8a693a7c0.yaml @@ -1,7 +1,7 @@ --- other: - | - :func:`qiskit.algorithms.aux_ops_evaluator.eval_observables` is added that originates from - a method previously residing in :class:`qiskit.algorithms.VQE`. The method is general enough so - that it can be used in other algorithms, for example time evolution algorithms. The method is - also refactored to be more modular. + :func:`qiskit.algorithms.eval_observables` is added that originates from a method previously + residing in :class:`qiskit.algorithms.VQE`. The method is general enough so that it can be used + in other algorithms, for example time evolution algorithms. The method is also refactored to be + more modular. diff --git a/test/python/algorithms/test_aux_ops_evaluator.py b/test/python/algorithms/test_aux_ops_evaluator.py index 7491cedfac8e..8fac09f6fd7d 100644 --- a/test/python/algorithms/test_aux_ops_evaluator.py +++ b/test/python/algorithms/test_aux_ops_evaluator.py @@ -17,7 +17,7 @@ import numpy as np from ddt import ddt, data, unpack -from qiskit.algorithms.aux_ops_evaluator import eval_observables +from qiskit.algorithms import eval_observables from qiskit import BasicAer from qiskit.circuit.library import EfficientSU2 from qiskit.opflow import PauliSumOp, X, Z, I, ExpectationFactory From 80846833a72e08ffd6660f7fa6704ab961e38562 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 11 Mar 2022 08:00:14 +0100 Subject: [PATCH 091/145] Improved unit test. --- test/python/algorithms/test_aux_ops_evaluator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/algorithms/test_aux_ops_evaluator.py b/test/python/algorithms/test_aux_ops_evaluator.py index 8fac09f6fd7d..ad5e23af7061 100644 --- a/test/python/algorithms/test_aux_ops_evaluator.py +++ b/test/python/algorithms/test_aux_ops_evaluator.py @@ -101,7 +101,7 @@ def test_eval_observables_statevector(self, observables, expected_result): quantum_instance, bound_ansatz, observables, expectation, self.threshold ) - if isinstance(result, dict): + if isinstance(observables, dict): np.testing.assert_equal(list(result.keys()), list(expected_result.keys())) np.testing.assert_array_almost_equal( list(result.values()), list(expected_result.values()), decimal=decimal From 25cdbecf29ded44ffa542f584aeda93bf839b4e8 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 11 Mar 2022 12:47:19 +0100 Subject: [PATCH 092/145] Added quantum instance support. --- .../algorithms/evolvers/evolution_result.py | 4 +- .../real/trotterization/trotter_qrte.py | 26 ++++++++++--- .../trotterization/test_trotter_qrte.py | 37 +++++++++++++++---- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_result.py b/qiskit/algorithms/evolvers/evolution_result.py index ead37fd98dfc..1dd91d705d28 100644 --- a/qiskit/algorithms/evolvers/evolution_result.py +++ b/qiskit/algorithms/evolvers/evolution_result.py @@ -16,7 +16,7 @@ from qiskit import QuantumCircuit from qiskit.algorithms.list_or_dict import ListOrDict -from qiskit.opflow import StateFn +from qiskit.opflow import StateFn, OperatorBase from ..algorithm_result import AlgorithmResult @@ -25,7 +25,7 @@ class EvolutionResult(AlgorithmResult): def __init__( self, - evolved_state: Union[StateFn, QuantumCircuit], + evolved_state: Union[StateFn, QuantumCircuit, OperatorBase], aux_ops_evaluated: Optional[ListOrDict[Tuple[complex, complex]]] = None, ): """ diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index f32b89e93759..6276f5af5052 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -12,18 +12,21 @@ """An algorithm to implement a Trotterization real time-evolution.""" -from typing import Union, Dict +from typing import Union, Dict, Optional -from qiskit.algorithms import EvolutionProblem, EvolutionResult, RealEvolver +from qiskit.algorithms import EvolutionProblem, EvolutionResult, RealEvolver, eval_observables from qiskit.circuit import Parameter from qiskit.opflow import ( OperatorBase, SummedOp, PauliOp, CircuitOp, + ExpectationBase, CircuitSampler, ) from qiskit.circuit.library import PauliEvolutionGate +from qiskit.providers import Backend, BaseBackend from qiskit.synthesis import ProductFormula, LieTrotter +from qiskit.utils import QuantumInstance from .trotter_ops_validator import is_op_bound @@ -49,13 +52,21 @@ class TrotterQrte(RealEvolver): evolved_state = trotter_qrte.evolve().evolved_state """ - def __init__(self, product_formula: ProductFormula = LieTrotter()) -> None: + def __init__( + self, + quantum_instance: Union[QuantumInstance, BaseBackend, Backend], + product_formula: ProductFormula = LieTrotter(), + ) -> None: """ Args: + quantum_instance: A quantum instance used for calculations. product_formula: A Lie-Trotter-Suzuki product formula. The default is the Lie-Trotter first order product formula with a single repetition. """ self.product_formula = product_formula + self._quantum_instance = quantum_instance + + self._circuit_sampler = CircuitSampler(quantum_instance) # TODO aux ops def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: @@ -88,10 +99,13 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: ) if evolution_problem.initial_state is not None: - evolved_state = (evolution_gate @ evolution_problem.initial_state).eval() - return EvolutionResult(evolved_state) + quantum_state = evolution_gate @ evolution_problem.initial_state + evolved_state = self._circuit_sampler.convert(quantum_state).eval() + + else: + raise ValueError("initial_state must be provided.") - raise ValueError("initial_state must be provided.") + return EvolutionResult(evolved_state) @staticmethod def _try_binding_params( diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index e305377932eb..4cf6805598d4 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -14,6 +14,7 @@ import unittest +from qiskit import BasicAer from test.python.opflow import QiskitOpflowTestCase from ddt import ddt, data import numpy as np @@ -25,7 +26,7 @@ TrotterQrte, ) from qiskit.quantum_info import Statevector -from qiskit.utils import algorithm_globals +from qiskit.utils import algorithm_globals, QuantumInstance from qiskit.circuit import Parameter from qiskit.opflow import ( X, @@ -43,11 +44,33 @@ class TestTrotterQrte(QiskitOpflowTestCase): """Trotter Qrte tests.""" + def setUp(self): + super().setUp() + self.seed = 50 + algorithm_globals.random_seed = self.seed + self.h2_op = ( + -1.052373245772859 * (I ^ I) + + 0.39793742484318045 * (I ^ Z) + - 0.39793742484318045 * (Z ^ I) + - 0.01128010425623538 * (Z ^ Z) + + 0.18093119978423156 * (X ^ X) + ) + + shots = 1 + backend = BasicAer.get_backend("statevector_simulator") + self.quantum_instance = QuantumInstance( + backend=backend, + shots=shots, + seed_simulator=self.seed, + seed_transpiler=self.seed, + ) + def test_trotter_qrte_trotter(self): """Test for trotter qrte.""" operator = X + Z # LieTrotter with 1 rep - trotter_qrte = TrotterQrte() + + trotter_qrte = TrotterQrte(self.quantum_instance) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -63,7 +86,7 @@ def test_trotter_qrte_trotter(self): def test_trotter_qrte_trotter_2(self, operator): """Test for trotter qrte.""" # LieTrotter with 1 rep - trotter_qrte = TrotterQrte() + trotter_qrte = TrotterQrte(self.quantum_instance) initial_state = StateFn([1, 0, 0, 0]) # Calculate the expected state @@ -79,7 +102,7 @@ def test_trotter_qrte_suzuki(self): """Test for trotter qrte with Suzuki.""" operator = X + Z # 2nd order Suzuki with 1 rep - trotter_qrte = TrotterQrte(SuzukiTrotter()) + trotter_qrte = TrotterQrte(self.quantum_instance, SuzukiTrotter()) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -99,7 +122,7 @@ def test_trotter_qrte_qdrift(self): algorithm_globals.random_seed = 0 operator = X + Z # QDrift with one repetition - trotter_qrte = TrotterQrte(QDrift()) + trotter_qrte = TrotterQrte(self.quantum_instance, QDrift()) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -117,7 +140,7 @@ def test_trotter_qrte_trotter_binding_missing_dict(self): """Test for trotter qrte with binding and missing dictionary..""" t_param = Parameter("t") operator = X * t_param + Z - trotter_qrte = TrotterQrte() + trotter_qrte = TrotterQrte(self.quantum_instance) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state, t_param=t_param) with assert_raises(ValueError): @@ -127,7 +150,7 @@ def test_trotter_qrte_trotter_binding_missing_param(self): """Test for trotter qrte with binding and missing param.""" t_param = Parameter("t") operator = X * t_param + Z - trotter_qrte = TrotterQrte() + trotter_qrte = TrotterQrte(self.quantum_instance) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) with assert_raises(ValueError): From 76e0a19e7ff244d5724e10d5136e47d27abea36a Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 11 Mar 2022 13:45:14 +0100 Subject: [PATCH 093/145] Added support for aux_ops and unit test. --- qiskit/algorithms/aux_ops_evaluator.py | 14 ++++----- .../real/trotterization/trotter_qrte.py | 28 +++++++++++++++-- .../trotterization/test_trotter_qrte.py | 31 ++++++++++++++----- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py index 931ac2c91241..471058e7b42e 100644 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -24,6 +24,7 @@ CircuitStateFn, OperatorBase, ExpectationBase, + VectorStateFn, ) from qiskit.providers import BaseBackend, Backend from qiskit.utils import QuantumInstance @@ -59,19 +60,18 @@ def eval_observables( ValueError: If a ``quantum_state`` with free parameters is provided. """ - if len(quantum_state.parameters) > 0: - raise ValueError( - "A parametrized quantum circuit representing a quantum_state was provided. It is not " - "allowed - the circuit cannot have free parameters." - ) - # Create new CircuitSampler to avoid breaking existing one's caches. sampler = CircuitSampler(quantum_instance) list_op = _prepare_list_op(observables) + if isinstance(quantum_state, VectorStateFn): + quantum_state = quantum_state.to_circuit_op() + elif isinstance(quantum_state, QuantumCircuit): + quantum_state = CircuitStateFn(quantum_state) + observables_expect = expectation.convert( - StateFn(list_op, is_measurement=True).compose(CircuitStateFn(quantum_state)) + StateFn(list_op, is_measurement=True).compose(quantum_state) ) observables_expect_sampled = sampler.convert(observables_expect) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 6276f5af5052..bd04c1da018f 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -21,7 +21,8 @@ SummedOp, PauliOp, CircuitOp, - ExpectationBase, CircuitSampler, + ExpectationBase, + CircuitSampler, ) from qiskit.circuit.library import PauliEvolutionGate from qiskit.providers import Backend, BaseBackend @@ -56,18 +57,26 @@ def __init__( self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend], product_formula: ProductFormula = LieTrotter(), + expectation: Optional[ExpectationBase] = None, ) -> None: """ Args: quantum_instance: A quantum instance used for calculations. product_formula: A Lie-Trotter-Suzuki product formula. The default is the Lie-Trotter first order product formula with a single repetition. + expectation: An instance of ExpectationBase which defines a method for calculating + expectation values of EvolutionProblem.aux_operators. """ self.product_formula = product_formula self._quantum_instance = quantum_instance + self._expectation = expectation self._circuit_sampler = CircuitSampler(quantum_instance) + @classmethod + def supports_aux_operators(cls) -> bool: + return True + # TODO aux ops def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: """ @@ -105,7 +114,22 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: else: raise ValueError("initial_state must be provided.") - return EvolutionResult(evolved_state) + evaluated_aux_ops = None + if evolution_problem.aux_operators is not None: + if self._quantum_instance is not None and self._expectation is not None: + evaluated_aux_ops = eval_observables( + self._quantum_instance, + evolved_state, + evolution_problem.aux_operators, + self._expectation, + 1e-8, # TODO algorithms.global + ) + else: + raise ValueError( + "aux_operators where provided for evaluations but no expectation was provided." + ) + + return EvolutionResult(evolved_state, evaluated_aux_ops) @staticmethod def _try_binding_params( diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 4cf6805598d4..e80fcbe54753 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -36,6 +36,7 @@ StateFn, I, Y, + MatrixExpectation, ) from qiskit.synthesis import SuzukiTrotter, QDrift @@ -48,14 +49,6 @@ def setUp(self): super().setUp() self.seed = 50 algorithm_globals.random_seed = self.seed - self.h2_op = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - shots = 1 backend = BasicAer.get_backend("statevector_simulator") self.quantum_instance = QuantumInstance( @@ -82,6 +75,28 @@ def test_trotter_qrte_trotter(self): np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) + def test_trotter_qrte_trotter_aux_ops(self): + """Test for trotter qrte.""" + operator = X + Z + # LieTrotter with 1 rep + aux_ops = [X, Y] + expectation = MatrixExpectation() + trotter_qrte = TrotterQrte(self.quantum_instance, expectation=expectation) + initial_state = Zero + evolution_problem = EvolutionProblem(operator, 1, initial_state, aux_ops) + evolution_result = trotter_qrte.evolve(evolution_problem) + # Calculate the expected state + expected_state = ( + expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() + ) + expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) + expected_aux_ops_evaluated = [(0.8268218104318058, 0.0), (0.3784012476539641, 0.0)] + + np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) + np.testing.assert_array_almost_equal( + evolution_result.aux_ops_evaluated, expected_aux_ops_evaluated + ) + @data((X ^ Y) + (Y ^ X), (Z ^ Z) + (Z ^ I) + (I ^ Z), Y ^ Y) def test_trotter_qrte_trotter_2(self, operator): """Test for trotter qrte.""" From cb7201dc4f437ad2f219a3ad53fca74d3759f23c Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 14 Mar 2022 11:10:06 +0100 Subject: [PATCH 094/145] Fixed input object to eval_observables. --- qiskit/algorithms/aux_ops_evaluator.py | 7 +------ .../evolvers/real/trotterization/trotter_qrte.py | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py index 471058e7b42e..763338e0c738 100644 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -65,13 +65,8 @@ def eval_observables( list_op = _prepare_list_op(observables) - if isinstance(quantum_state, VectorStateFn): - quantum_state = quantum_state.to_circuit_op() - elif isinstance(quantum_state, QuantumCircuit): - quantum_state = CircuitStateFn(quantum_state) - observables_expect = expectation.convert( - StateFn(list_op, is_measurement=True).compose(quantum_state) + StateFn(list_op, is_measurement=True).compose(StateFn(quantum_state)) ) observables_expect_sampled = sampler.convert(observables_expect) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index bd04c1da018f..6bfdda8b5382 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -119,7 +119,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: if self._quantum_instance is not None and self._expectation is not None: evaluated_aux_ops = eval_observables( self._quantum_instance, - evolved_state, + quantum_state.primitive, evolution_problem.aux_operators, self._expectation, 1e-8, # TODO algorithms.global @@ -129,7 +129,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: "aux_operators where provided for evaluations but no expectation was provided." ) - return EvolutionResult(evolved_state, evaluated_aux_ops) + return EvolutionResult(evolved_state.eval(), evaluated_aux_ops) @staticmethod def _try_binding_params( From 8b81e364b621b4108cb166df1a64f819cc55efc3 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 14 Mar 2022 11:55:42 +0100 Subject: [PATCH 095/145] Fixed quantum_state types and conversion. --- qiskit/algorithms/aux_ops_evaluator.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py index 931ac2c91241..08ce31b0a363 100644 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -21,17 +21,21 @@ CircuitSampler, ListOp, StateFn, - CircuitStateFn, OperatorBase, ExpectationBase, ) from qiskit.providers import BaseBackend, Backend +from qiskit.quantum_info import Statevector from qiskit.utils import QuantumInstance def eval_observables( quantum_instance: Union[QuantumInstance, BaseBackend, Backend], - quantum_state: QuantumCircuit, + quantum_state: Union[ + Statevector, + QuantumCircuit, + OperatorBase, + ], observables: ListOrDict[OperatorBase], expectation: ExpectationBase, threshold: float = 1e-12, @@ -59,7 +63,12 @@ def eval_observables( ValueError: If a ``quantum_state`` with free parameters is provided. """ - if len(quantum_state.parameters) > 0: + if ( + isinstance( + quantum_state, (QuantumCircuit, OperatorBase) + ) # Statevector cannot be parametrized + and len(quantum_state.parameters) > 0 + ): raise ValueError( "A parametrized quantum circuit representing a quantum_state was provided. It is not " "allowed - the circuit cannot have free parameters." @@ -71,7 +80,7 @@ def eval_observables( list_op = _prepare_list_op(observables) observables_expect = expectation.convert( - StateFn(list_op, is_measurement=True).compose(CircuitStateFn(quantum_state)) + StateFn(list_op, is_measurement=True).compose(StateFn(quantum_state)) ) observables_expect_sampled = sampler.convert(observables_expect) @@ -86,7 +95,7 @@ def eval_observables( # Discard values below threshold observables_means = values * (np.abs(values) > threshold) # zip means and standard deviations into tuples - observables_results = zip(observables_means, std_devs) + observables_results = list(zip(observables_means, std_devs)) # Return None eigenvalues for None operators if observables is a list. # None operators are already dropped in compute_minimum_eigenvalue if observables is a dict. From b1b3bcbee11a29d1f6029085448401ea310ce42a Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 14 Mar 2022 13:45:17 +0100 Subject: [PATCH 096/145] Fixed quantum_state types and conversion. --- qiskit/algorithms/aux_ops_evaluator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py index 08ce31b0a363..351c0d1d849f 100644 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -70,8 +70,8 @@ def eval_observables( and len(quantum_state.parameters) > 0 ): raise ValueError( - "A parametrized quantum circuit representing a quantum_state was provided. It is not " - "allowed - the circuit cannot have free parameters." + "A parametrized representation of a quantum_state was provided. It is not " + "allowed - it cannot have free parameters." ) # Create new CircuitSampler to avoid breaking existing one's caches. From 12c6f9d03526eaf2e660bdefbed9f6cf5e59fb1d Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 14 Mar 2022 15:41:08 +0100 Subject: [PATCH 097/145] Fixed types and docs. --- qiskit/algorithms/aux_ops_evaluator.py | 2 -- .../real/trotterization/trotter_qrte.py | 20 +++++++++---------- qiskit/utils/algorithm_globals.py | 11 ++++++++++ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py index 763338e0c738..5d4340cb6a74 100644 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ b/qiskit/algorithms/aux_ops_evaluator.py @@ -21,10 +21,8 @@ CircuitSampler, ListOp, StateFn, - CircuitStateFn, OperatorBase, ExpectationBase, - VectorStateFn, ) from qiskit.providers import BaseBackend, Backend from qiskit.utils import QuantumInstance diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 6bfdda8b5382..9c9828d352a8 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -23,11 +23,12 @@ CircuitOp, ExpectationBase, CircuitSampler, + PauliSumOp, ) from qiskit.circuit.library import PauliEvolutionGate from qiskit.providers import Backend, BaseBackend from qiskit.synthesis import ProductFormula, LieTrotter -from qiskit.utils import QuantumInstance +from qiskit.utils import QuantumInstance, algorithm_globals from .trotter_ops_validator import is_op_bound @@ -77,7 +78,6 @@ def __init__( def supports_aux_operators(cls) -> bool: return True - # TODO aux ops def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: """ Evolves a quantum state for a given time using the Trotterization method @@ -85,7 +85,8 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: Time-dependent Hamiltonians are not yet supported. Args: - evolution_problem: Instance defining evolution problem. + evolution_problem: Instance defining evolution problem. For the included Hamiltonian, + only SummedOp, PauliOp, PauliSumOp are supported by TrotterQrte. Returns: Evolution result that includes an evolved state. @@ -122,7 +123,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: quantum_state.primitive, evolution_problem.aux_operators, self._expectation, - 1e-8, # TODO algorithms.global + algorithm_globals.numerical_tolerance_at_0, ) else: raise ValueError( @@ -133,16 +134,15 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: @staticmethod def _try_binding_params( - hamiltonian: Union[SummedOp, PauliOp, OperatorBase], + hamiltonian: Union[SummedOp, PauliOp, PauliSumOp], hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], ) -> Union[SummedOp, PauliOp, OperatorBase]: """ Tries binding parameters in a Hamiltonian. Args: - hamiltonian: The Hamiltonian of that defines an evolution. Can also be provided as list - of non-commuting operators where the elements are sums of commuting operators. - For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. + hamiltonian: The Hamiltonian of that defines an evolution. Only SummedOp, PauliOp, + PauliSumOp are supported by TrotterQrte. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to certain values. @@ -165,7 +165,7 @@ def _try_binding_params( op_list.append(op_bound) return sum(op_list) elif isinstance( - hamiltonian, (PauliOp, OperatorBase) + hamiltonian, (PauliOp, PauliSumOp) ): # in case there is only a single summand if hamiltonian_value_dict is not None: op_bound = hamiltonian.bind_parameters(hamiltonian_value_dict) @@ -177,5 +177,5 @@ def _try_binding_params( else: raise ValueError( f"Provided a Hamiltonian of an unsupported type: {type(hamiltonian)}. Only " - f"SummedOp, PauliOp, and OperatorBase base are supported by TrotterQrte." + f"SummedOp, PauliOp base are supported by TrotterQrte." ) diff --git a/qiskit/utils/algorithm_globals.py b/qiskit/utils/algorithm_globals.py index 98f1b4f0fffa..dbbbaa34c82d 100644 --- a/qiskit/utils/algorithm_globals.py +++ b/qiskit/utils/algorithm_globals.py @@ -35,6 +35,7 @@ def __init__(self) -> None: self._num_processes = QiskitAlgorithmGlobals.CPU_COUNT self._random = None self._massive = False + self._numerical_tolerance_at_0 = 1e-12 try: settings = get_config() self.num_processes = settings.get("num_processes", QiskitAlgorithmGlobals.CPU_COUNT) @@ -101,6 +102,16 @@ def massive(self, massive: bool) -> None: """Set massive to allow processing of large matrices or vectors.""" self._massive = massive + @property + def numerical_tolerance_at_0(self) -> float: + """Return numerical tolerance.""" + return self._numerical_tolerance_at_0 + + @numerical_tolerance_at_0.setter + def numerical_tolerance_at_0(self, numerical_tolerance_at_0: float) -> None: + """Set numerical tolerance.""" + self._numerical_tolerance_at_0 = numerical_tolerance_at_0 + # Global instance to be used as the entry point for globals. algorithm_globals = QiskitAlgorithmGlobals() From 4e50808de546801b41b8492cd409105fd17bb9d9 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 15 Mar 2022 07:50:07 +0100 Subject: [PATCH 098/145] Code refactoring. --- .../real/trotterization/trotter_ops_validator.py | 7 +++---- .../evolvers/real/trotterization/trotter_qrte.py | 15 ++++++--------- .../trotterization/test_trotter_qrte.py | 13 +++++++------ 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py index 253fbbc4cd5f..41169f61fbe6 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py @@ -17,13 +17,12 @@ from qiskit.circuit import Parameter, ParameterExpression from qiskit.opflow import ( - OperatorBase, SummedOp, PauliOp, ) -def is_op_bound(operator: Union[SummedOp, PauliOp, OperatorBase]) -> None: +def is_op_bound(operator: Union[SummedOp, PauliOp]) -> None: """Checks if an operator provided has all parameters bound. Args: @@ -39,7 +38,7 @@ def is_op_bound(operator: Union[SummedOp, PauliOp, OperatorBase]) -> None: ) -def validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBase]): +def validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp]): """Validates that a Hamiltonian is of a correct type and with expected dependence on parameters. @@ -61,7 +60,7 @@ def validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp, OperatorBase "Hamiltonian term has a coefficient that is not a linear function of a " "single parameter. It is not supported." ) - elif isinstance(hamiltonian, (PauliOp, OperatorBase)): + elif isinstance(hamiltonian, PauliOp): if not _is_pauli_lin_single_param(hamiltonian): raise ValueError( "Hamiltonian term has a coefficient that is not a linear function of a " diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 9c9828d352a8..865d605f4390 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -23,7 +23,6 @@ CircuitOp, ExpectationBase, CircuitSampler, - PauliSumOp, ) from qiskit.circuit.library import PauliEvolutionGate from qiskit.providers import Backend, BaseBackend @@ -86,7 +85,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: Args: evolution_problem: Instance defining evolution problem. For the included Hamiltonian, - only SummedOp, PauliOp, PauliSumOp are supported by TrotterQrte. + only SummedOp, PauliOp are supported by TrotterQrte. Returns: Evolution result that includes an evolved state. @@ -134,15 +133,15 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: @staticmethod def _try_binding_params( - hamiltonian: Union[SummedOp, PauliOp, PauliSumOp], + hamiltonian: Union[SummedOp, PauliOp], hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], ) -> Union[SummedOp, PauliOp, OperatorBase]: """ Tries binding parameters in a Hamiltonian. Args: - hamiltonian: The Hamiltonian of that defines an evolution. Only SummedOp, PauliOp, - PauliSumOp are supported by TrotterQrte. + hamiltonian: The Hamiltonian of that defines an evolution. Only SummedOp, PauliOp are + supported by TrotterQrte. hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to certain values. @@ -164,9 +163,7 @@ def _try_binding_params( is_op_bound(op_bound) op_list.append(op_bound) return sum(op_list) - elif isinstance( - hamiltonian, (PauliOp, PauliSumOp) - ): # in case there is only a single summand + elif isinstance(hamiltonian, PauliOp): # in case there is only a single summand if hamiltonian_value_dict is not None: op_bound = hamiltonian.bind_parameters(hamiltonian_value_dict) else: @@ -177,5 +174,5 @@ def _try_binding_params( else: raise ValueError( f"Provided a Hamiltonian of an unsupported type: {type(hamiltonian)}. Only " - f"SummedOp, PauliOp base are supported by TrotterQrte." + f"SummedOp, PauliOp are supported by TrotterQrte." ) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index e80fcbe54753..3cc874475374 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -14,13 +14,13 @@ import unittest -from qiskit import BasicAer from test.python.opflow import QiskitOpflowTestCase from ddt import ddt, data import numpy as np from numpy.testing import assert_raises from scipy.linalg import expm +from qiskit import BasicAer from qiskit.algorithms import EvolutionProblem from qiskit.algorithms.evolvers.real.trotterization.trotter_qrte import ( TrotterQrte, @@ -37,6 +37,7 @@ I, Y, MatrixExpectation, + SummedOp, ) from qiskit.synthesis import SuzukiTrotter, QDrift @@ -60,7 +61,7 @@ def setUp(self): def test_trotter_qrte_trotter(self): """Test for trotter qrte.""" - operator = X + Z + operator = SummedOp([X, Z]) # LieTrotter with 1 rep trotter_qrte = TrotterQrte(self.quantum_instance) @@ -77,7 +78,7 @@ def test_trotter_qrte_trotter(self): def test_trotter_qrte_trotter_aux_ops(self): """Test for trotter qrte.""" - operator = X + Z + operator = SummedOp([X, Z]) # LieTrotter with 1 rep aux_ops = [X, Y] expectation = MatrixExpectation() @@ -97,7 +98,7 @@ def test_trotter_qrte_trotter_aux_ops(self): evolution_result.aux_ops_evaluated, expected_aux_ops_evaluated ) - @data((X ^ Y) + (Y ^ X), (Z ^ Z) + (Z ^ I) + (I ^ Z), Y ^ Y) + @data(SummedOp([(X ^ Y), (Y ^ X)]), SummedOp([(Z ^ Z), (Z ^ I), (I ^ Z)]), Y ^ Y) def test_trotter_qrte_trotter_2(self, operator): """Test for trotter qrte.""" # LieTrotter with 1 rep @@ -115,7 +116,7 @@ def test_trotter_qrte_trotter_2(self, operator): def test_trotter_qrte_suzuki(self): """Test for trotter qrte with Suzuki.""" - operator = X + Z + operator = SummedOp([X, Z]) # 2nd order Suzuki with 1 rep trotter_qrte = TrotterQrte(self.quantum_instance, SuzukiTrotter()) initial_state = Zero @@ -135,7 +136,7 @@ def test_trotter_qrte_suzuki(self): def test_trotter_qrte_qdrift(self): """Test for trotter qrte with QDrift.""" algorithm_globals.random_seed = 0 - operator = X + Z + operator = SummedOp([X, Z]) # QDrift with one repetition trotter_qrte = TrotterQrte(self.quantum_instance, QDrift()) initial_state = Zero From 9e1c09babb801b114f535834ec3bab26e091de7d Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 22 Mar 2022 10:12:53 +0100 Subject: [PATCH 099/145] Lint fix. --- .../algorithms/evolvers/real/trotterization/trotter_qrte.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 865d605f4390..b31d6f08536a 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -75,6 +75,12 @@ def __init__( @classmethod def supports_aux_operators(cls) -> bool: + """ + Whether computing the expectation value of auxiliary operators is supported. + + Returns: + True if aux_operator expectations can be evaluated, False otherwise. + """ return True def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: From dc835d9fb13bdd4cae3f0f5cbfc0da1192b296fe Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 22 Mar 2022 11:04:36 +0100 Subject: [PATCH 100/145] Code refactoring. --- qiskit/algorithms/__init__.py | 3 + .../algorithms/evolvers/evolution_problem.py | 5 +- .../trotterization/trotter_ops_validator.py | 23 +++--- .../real/trotterization/trotter_qrte.py | 73 +++++++++++++------ .../trotterization/test_trotter_qrte.py | 16 ++-- 5 files changed, 77 insertions(+), 43 deletions(-) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index 0c3ed197f2c6..dfc76b7b2e0b 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -106,6 +106,7 @@ RealEvolver ImaginaryEvolver + TrotterQrte EvolutionResult EvolutionProblem @@ -243,6 +244,7 @@ ) from .exceptions import AlgorithmError from .aux_ops_evaluator import eval_observables +from .evolvers.real.trotterization import TrotterQrte __all__ = [ "AlgorithmResult", @@ -266,6 +268,7 @@ "NumPyEigensolver", "RealEvolver", "ImaginaryEvolver", + "TrotterQrte", "EvolutionResult", "EvolutionProblem", "LinearSolverResult", diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index f926dbdf3d96..7f2da1a159aa 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -23,9 +23,8 @@ class EvolutionProblem: """Evolution problem class. - This class is the input to time evolution algorithms and contains - information on e.g. the total evolution time and under which Hamiltonian - the state is evolved. + This class is the input to time evolution algorithms and must contain information on the total + evolution time, a quantum state to be evolved and under which Hamiltonian the state is evolved. """ def __init__( diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py index 41169f61fbe6..fd296350cc9e 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py @@ -48,26 +48,27 @@ def validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp]): Raises: ValueError: if an invalid Hamiltonian is provided. """ + value_error = ValueError( + "Hamiltonian term has a coefficient that is not a linear function of a " + "single parameter. It is not supported." + ) if isinstance(hamiltonian, SummedOp): if isinstance(hamiltonian.coeff, ParameterExpression): raise ValueError( - "The coefficient multiplying the whole Hamiltonian cannot be a " - "ParameterExpression." + f"The coefficient multiplying the whole Hamiltonian cannot be a " + f"ParameterExpression. The following coefficient was detected: {hamiltonian.coeff}." ) for op in hamiltonian.oplist: if not _is_pauli_lin_single_param(op): - raise ValueError( - "Hamiltonian term has a coefficient that is not a linear function of a " - "single parameter. It is not supported." - ) + raise value_error elif isinstance(hamiltonian, PauliOp): if not _is_pauli_lin_single_param(hamiltonian): - raise ValueError( - "Hamiltonian term has a coefficient that is not a linear function of a " - "single parameter. It is not supported." - ) + raise value_error else: - raise ValueError("Hamiltonian not a SummedOp/PauliOp which are the only options supported.") + raise ValueError( + f"Hamiltonian not a SummedOp/PauliOp which are the only options supported. The " + f"following type detected instead: {type(hamiltonian)}." + ) def _is_pauli_lin_single_param(operator: PauliOp) -> bool: diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index b31d6f08536a..fe03034e718b 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -55,31 +55,54 @@ class TrotterQrte(RealEvolver): def __init__( self, - quantum_instance: Union[QuantumInstance, BaseBackend, Backend], - product_formula: ProductFormula = LieTrotter(), + product_formula: ProductFormula = None, expectation: Optional[ExpectationBase] = None, + quantum_instance: Union[QuantumInstance, BaseBackend, Backend] = None, ) -> None: """ Args: - quantum_instance: A quantum instance used for calculations. product_formula: A Lie-Trotter-Suzuki product formula. The default is the Lie-Trotter first order product formula with a single repetition. expectation: An instance of ExpectationBase which defines a method for calculating expectation values of EvolutionProblem.aux_operators. + quantum_instance: A quantum instance used for calculations. """ - self.product_formula = product_formula + if product_formula is None: + product_formula = LieTrotter() + self._product_formula = product_formula self._quantum_instance = quantum_instance self._expectation = expectation self._circuit_sampler = CircuitSampler(quantum_instance) + @property + def product_formula(self) -> ProductFormula: + """Returns a product formula used in the algorithm.""" + return self._product_formula + + @property + def quantum_instance(self) -> Union[QuantumInstance, BaseBackend, Backend]: + """Returns a quantum instance used in the algorithm.""" + return self._quantum_instance + + @quantum_instance.setter + def quantum_instance(self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend]): + """Sets a quantum instance used in the algorithm.""" + self._quantum_instance = quantum_instance + + @property + def expectation(self) -> ExpectationBase: + """Returns an expectation used in the algorithm.""" + return self._expectation + @classmethod def supports_aux_operators(cls) -> bool: """ Whether computing the expectation value of auxiliary operators is supported. Returns: - True if aux_operator expectations can be evaluated, False otherwise. + True if `aux_operators` expectations in the EvolutionProblem can be evaluated, False + otherwise. """ return True @@ -87,7 +110,8 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: """ Evolves a quantum state for a given time using the Trotterization method based on a product formula provided. - Time-dependent Hamiltonians are not yet supported. + + Note: Time-dependent Hamiltonians are not yet supported. Args: evolution_problem: Instance defining evolution problem. For the included Hamiltonian, @@ -97,12 +121,22 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: Evolution result that includes an evolved state. Raises: - ValueError: If t_param is not set to None (feature not currently supported). + ValueError: If `t_param` is not set to None in the EvolutionProblem (feature not + currently supported). + ValueError: If the `initial_state` is not provided in the EvolutionProblem. """ if evolution_problem.t_param is not None: raise ValueError( "TrotterQrte does not accept a time dependent hamiltonian," - "t_param should be set to None." + "`t_param` from the EvolutionProblem should be set to None." + ) + + if evolution_problem.aux_operators is not None and ( + self._quantum_instance is None or self._expectation is None + ): + raise ValueError( + "aux_operators where provided for evaluations but no `expectation` or " + "`quantum_instance` was provided." ) hamiltonian = self._try_binding_params( @@ -110,7 +144,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: ) # the evolution gate evolution_gate = CircuitOp( - PauliEvolutionGate(hamiltonian, evolution_problem.time, synthesis=self.product_formula) + PauliEvolutionGate(hamiltonian, evolution_problem.time, synthesis=self._product_formula) ) if evolution_problem.initial_state is not None: @@ -118,22 +152,17 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: evolved_state = self._circuit_sampler.convert(quantum_state).eval() else: - raise ValueError("initial_state must be provided.") + raise ValueError("`initial_state` must be provided in the EvolutionProblem.") evaluated_aux_ops = None if evolution_problem.aux_operators is not None: - if self._quantum_instance is not None and self._expectation is not None: - evaluated_aux_ops = eval_observables( - self._quantum_instance, - quantum_state.primitive, - evolution_problem.aux_operators, - self._expectation, - algorithm_globals.numerical_tolerance_at_0, - ) - else: - raise ValueError( - "aux_operators where provided for evaluations but no expectation was provided." - ) + evaluated_aux_ops = eval_observables( + self._quantum_instance, + quantum_state.primitive, + evolution_problem.aux_operators, + self._expectation, + algorithm_globals.numerical_tolerance_at_0, + ) return EvolutionResult(evolved_state.eval(), evaluated_aux_ops) diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py index 3cc874475374..ac15a442862b 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py @@ -64,7 +64,7 @@ def test_trotter_qrte_trotter(self): operator = SummedOp([X, Z]) # LieTrotter with 1 rep - trotter_qrte = TrotterQrte(self.quantum_instance) + trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -82,7 +82,7 @@ def test_trotter_qrte_trotter_aux_ops(self): # LieTrotter with 1 rep aux_ops = [X, Y] expectation = MatrixExpectation() - trotter_qrte = TrotterQrte(self.quantum_instance, expectation=expectation) + trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance, expectation=expectation) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state, aux_ops) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -102,7 +102,7 @@ def test_trotter_qrte_trotter_aux_ops(self): def test_trotter_qrte_trotter_2(self, operator): """Test for trotter qrte.""" # LieTrotter with 1 rep - trotter_qrte = TrotterQrte(self.quantum_instance) + trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance) initial_state = StateFn([1, 0, 0, 0]) # Calculate the expected state @@ -118,7 +118,9 @@ def test_trotter_qrte_suzuki(self): """Test for trotter qrte with Suzuki.""" operator = SummedOp([X, Z]) # 2nd order Suzuki with 1 rep - trotter_qrte = TrotterQrte(self.quantum_instance, SuzukiTrotter()) + trotter_qrte = TrotterQrte( + quantum_instance=self.quantum_instance, product_formula=SuzukiTrotter() + ) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -138,7 +140,7 @@ def test_trotter_qrte_qdrift(self): algorithm_globals.random_seed = 0 operator = SummedOp([X, Z]) # QDrift with one repetition - trotter_qrte = TrotterQrte(self.quantum_instance, QDrift()) + trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance, product_formula=QDrift()) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -156,7 +158,7 @@ def test_trotter_qrte_trotter_binding_missing_dict(self): """Test for trotter qrte with binding and missing dictionary..""" t_param = Parameter("t") operator = X * t_param + Z - trotter_qrte = TrotterQrte(self.quantum_instance) + trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state, t_param=t_param) with assert_raises(ValueError): @@ -166,7 +168,7 @@ def test_trotter_qrte_trotter_binding_missing_param(self): """Test for trotter qrte with binding and missing param.""" t_param = Parameter("t") operator = X * t_param + Z - trotter_qrte = TrotterQrte(self.quantum_instance) + trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) with assert_raises(ValueError): From 5301b6849cb936ad61409575873c05cdca36c353 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 22 Mar 2022 11:07:23 +0100 Subject: [PATCH 101/145] Reno update. --- .../notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml b/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml index 595e4d36f83a..8f26f5d1bc6f 100644 --- a/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml +++ b/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml @@ -1,7 +1,7 @@ --- features: - | - Added Trotterization-based Quantum Real Time Evolution Algorithm. It is compliant with the new - Quantum Time Evolution Framework and makes use of recent - :class:`qiskit.synthesis.evolution.ProductFormula` and + Added Trotterization-based Quantum Real Time Evolution Algorithm + :class:`qiskit.algorithms.TrotterQrte`. It is compliant with the new Quantum Time Evolution + Framework and makes use of :class:`qiskit.synthesis.evolution.ProductFormula` and :class:`qiskit.circuit.library.PauliEvolutionGate` implementations. From f76181ea68b835032190be3b4db21c061bf97f2f Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 22 Mar 2022 16:18:06 +0100 Subject: [PATCH 102/145] Code refactoring. --- qiskit/algorithms/__init__.py | 4 +-- .../evolvers/real/trotterization/__init__.py | 4 +-- .../trotterization/trotter_ops_validator.py | 2 +- .../real/trotterization/trotter_qrte.py | 32 +++++++++---------- .../quantum_time_evolution/real/__init__.py | 12 ------- .../real}/__init__.py | 0 .../real/trotterization}/__init__.py | 0 .../test_trotter_ops_validator.py | 0 .../real}/trotterization/test_trotter_qrte.py | 18 +++++------ .../real/implementations/__init__.py | 11 ------- .../trotterization/__init__.py | 11 ------- 11 files changed, 29 insertions(+), 65 deletions(-) delete mode 100644 qiskit/algorithms/quantum_time_evolution/real/__init__.py rename test/python/algorithms/{quantum_time_evolution => evolvers/real}/__init__.py (100%) rename test/python/algorithms/{quantum_time_evolution/real => evolvers/real/trotterization}/__init__.py (100%) rename test/python/algorithms/{quantum_time_evolution/real/implementations => evolvers/real}/trotterization/test_trotter_ops_validator.py (100%) rename test/python/algorithms/{quantum_time_evolution/real/implementations => evolvers/real}/trotterization/test_trotter_qrte.py (93%) delete mode 100644 test/python/algorithms/quantum_time_evolution/real/implementations/__init__.py delete mode 100644 test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index dfc76b7b2e0b..0d71a3678dd4 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -244,7 +244,7 @@ ) from .exceptions import AlgorithmError from .aux_ops_evaluator import eval_observables -from .evolvers.real.trotterization import TrotterQrte +from .evolvers.real.trotterization import TrotterQRTE __all__ = [ "AlgorithmResult", @@ -268,7 +268,7 @@ "NumPyEigensolver", "RealEvolver", "ImaginaryEvolver", - "TrotterQrte", + "TrotterQRTE", "EvolutionResult", "EvolutionProblem", "LinearSolverResult", diff --git a/qiskit/algorithms/evolvers/real/trotterization/__init__.py b/qiskit/algorithms/evolvers/real/trotterization/__init__.py index 77ca4a71d5d1..5bbe0f72c3e2 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/__init__.py +++ b/qiskit/algorithms/evolvers/real/trotterization/__init__.py @@ -19,7 +19,7 @@ Hamiltonian using an expectation value through a custom observable.""" from qiskit.algorithms.evolvers.real.trotterization.trotter_qrte import ( - TrotterQrte, + TrotterQRTE, ) -__all__ = ["TrotterQrte"] +__all__ = ["TrotterQRTE"] diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py index fd296350cc9e..73d21453ea2d 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py @@ -31,7 +31,7 @@ def is_op_bound(operator: Union[SummedOp, PauliOp]) -> None: Raises: ValueError: If an operator has unbound parameters. """ - if len(operator.parameters) > 0: + if len(operator.parameters) != 0: raise ValueError( f"Did not manage to bind all parameters in the Hamiltonian, " f"these parameters encountered: {operator.parameters}." diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index fe03034e718b..d9ef255d3939 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -28,11 +28,11 @@ from qiskit.providers import Backend, BaseBackend from qiskit.synthesis import ProductFormula, LieTrotter from qiskit.utils import QuantumInstance, algorithm_globals -from .trotter_ops_validator import is_op_bound +from .trotter_ops_validator import is_op_bound, validate_hamiltonian_form -class TrotterQrte(RealEvolver): - """ Class for performing Quantum Real Time Evolution using Trotterization. +class TrotterQRTE(RealEvolver): + """Class for performing Quantum Real Time Evolution using Trotterization. Type of Trotterization is defined by a ProductFormula provided. Examples: @@ -40,24 +40,26 @@ class TrotterQrte(RealEvolver): .. jupyter-execute:: from qiskit.opflow import X, Y, Zero - from qiskit.algorithms import EvolutionProblem, EvolutionResult - from qiskit.algorithms.evolvers.real.implementations.\ - trotterization.trotter_qrte import TrotterQrte + from qiskit.algorithms import EvolutionProblem, TrotterQRTE + from qiskit import BasicAer + from qiskit.utils import QuantumInstance operator = X + Z initial_state = Zero time = 1 evolution_problem = EvolutionProblem(operator, 1, initial_state) # LieTrotter with 1 rep - trotter_qrte = TrotterQrte(evolution_problem) - evolved_state = trotter_qrte.evolve().evolved_state + backend = BasicAer.get_backend("statevector_simulator") + quantum_instance = QuantumInstance(backend=backend) + trotter_qrte = TrotterQRTE(quantum_instance=quantum_instance) + evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state """ def __init__( self, - product_formula: ProductFormula = None, + product_formula: Optional[ProductFormula] = None, expectation: Optional[ExpectationBase] = None, - quantum_instance: Union[QuantumInstance, BaseBackend, Backend] = None, + quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None, ) -> None: """ Args: @@ -111,7 +113,8 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: Evolves a quantum state for a given time using the Trotterization method based on a product formula provided. - Note: Time-dependent Hamiltonians are not yet supported. + .. note:: + Time-dependent Hamiltonians are not yet supported. Args: evolution_problem: Instance defining evolution problem. For the included Hamiltonian, @@ -138,7 +141,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: "aux_operators where provided for evaluations but no `expectation` or " "`quantum_instance` was provided." ) - + validate_hamiltonian_form(evolution_problem.hamiltonian) hamiltonian = self._try_binding_params( evolution_problem.hamiltonian, evolution_problem.hamiltonian_value_dict ) @@ -206,8 +209,3 @@ def _try_binding_params( is_op_bound(op_bound) return op_bound - else: - raise ValueError( - f"Provided a Hamiltonian of an unsupported type: {type(hamiltonian)}. Only " - f"SummedOp, PauliOp are supported by TrotterQrte." - ) diff --git a/qiskit/algorithms/quantum_time_evolution/real/__init__.py b/qiskit/algorithms/quantum_time_evolution/real/__init__.py deleted file mode 100644 index 156d4bf6c9a7..000000000000 --- a/qiskit/algorithms/quantum_time_evolution/real/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -""" Quantum Real Time Evolution package """ diff --git a/test/python/algorithms/quantum_time_evolution/__init__.py b/test/python/algorithms/evolvers/real/__init__.py similarity index 100% rename from test/python/algorithms/quantum_time_evolution/__init__.py rename to test/python/algorithms/evolvers/real/__init__.py diff --git a/test/python/algorithms/quantum_time_evolution/real/__init__.py b/test/python/algorithms/evolvers/real/trotterization/__init__.py similarity index 100% rename from test/python/algorithms/quantum_time_evolution/real/__init__.py rename to test/python/algorithms/evolvers/real/trotterization/__init__.py diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_ops_validator.py similarity index 100% rename from test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_ops_validator.py rename to test/python/algorithms/evolvers/real/trotterization/test_trotter_ops_validator.py diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py similarity index 93% rename from test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py rename to test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index ac15a442862b..f164b8923a63 100644 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -23,7 +23,7 @@ from qiskit import BasicAer from qiskit.algorithms import EvolutionProblem from qiskit.algorithms.evolvers.real.trotterization.trotter_qrte import ( - TrotterQrte, + TrotterQRTE, ) from qiskit.quantum_info import Statevector from qiskit.utils import algorithm_globals, QuantumInstance @@ -43,7 +43,7 @@ @ddt -class TestTrotterQrte(QiskitOpflowTestCase): +class TestTrotterQRTE(QiskitOpflowTestCase): """Trotter Qrte tests.""" def setUp(self): @@ -64,7 +64,7 @@ def test_trotter_qrte_trotter(self): operator = SummedOp([X, Z]) # LieTrotter with 1 rep - trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance) + trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -82,7 +82,7 @@ def test_trotter_qrte_trotter_aux_ops(self): # LieTrotter with 1 rep aux_ops = [X, Y] expectation = MatrixExpectation() - trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance, expectation=expectation) + trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance, expectation=expectation) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state, aux_ops) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -102,7 +102,7 @@ def test_trotter_qrte_trotter_aux_ops(self): def test_trotter_qrte_trotter_2(self, operator): """Test for trotter qrte.""" # LieTrotter with 1 rep - trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance) + trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = StateFn([1, 0, 0, 0]) # Calculate the expected state @@ -118,7 +118,7 @@ def test_trotter_qrte_suzuki(self): """Test for trotter qrte with Suzuki.""" operator = SummedOp([X, Z]) # 2nd order Suzuki with 1 rep - trotter_qrte = TrotterQrte( + trotter_qrte = TrotterQRTE( quantum_instance=self.quantum_instance, product_formula=SuzukiTrotter() ) initial_state = Zero @@ -140,7 +140,7 @@ def test_trotter_qrte_qdrift(self): algorithm_globals.random_seed = 0 operator = SummedOp([X, Z]) # QDrift with one repetition - trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance, product_formula=QDrift()) + trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance, product_formula=QDrift()) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -158,7 +158,7 @@ def test_trotter_qrte_trotter_binding_missing_dict(self): """Test for trotter qrte with binding and missing dictionary..""" t_param = Parameter("t") operator = X * t_param + Z - trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance) + trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state, t_param=t_param) with assert_raises(ValueError): @@ -168,7 +168,7 @@ def test_trotter_qrte_trotter_binding_missing_param(self): """Test for trotter qrte with binding and missing param.""" t_param = Parameter("t") operator = X * t_param + Z - trotter_qrte = TrotterQrte(quantum_instance=self.quantum_instance) + trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) with assert_raises(ValueError): diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/__init__.py b/test/python/algorithms/quantum_time_evolution/real/implementations/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py b/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/test/python/algorithms/quantum_time_evolution/real/implementations/trotterization/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. From 2b0a7f13225226c549b914b03d0bf8d84c93fc29 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 23 Mar 2022 13:27:53 +0100 Subject: [PATCH 103/145] Implemented some CR feedback. --- .../algorithms/evolvers/evolution_problem.py | 19 ++++- .../evolvers/real/trotterization/__init__.py | 11 +-- .../real/trotterization/trotter_qrte.py | 78 +++++++++---------- .../real/trotterization/test_trotter_qrte.py | 47 +++++++---- 4 files changed, 94 insertions(+), 61 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 7f2da1a159aa..078424249d97 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -47,9 +47,26 @@ def __init__( free parameter must be within the ``hamiltonian``. hamiltonian_value_dict: If the Hamiltonian contains free parameters, this dictionary maps all these parameters to values. - """ + Raises: + ValueError: If not all parameter values are provided. + ValueError: If non-positive time of evolution is provided. + """ + t_param_set = set() + if t_param is not None: + t_param_set.add(t_param) + hamiltonian_dict_param_set = set() + if hamiltonian_value_dict is not None: + hamiltonian_dict_param_set.add(hamiltonian_value_dict.keys()) + params_set = t_param_set.union(hamiltonian_dict_param_set) + hamiltonian_param_set = set(hamiltonian.parameters) + if hamiltonian_param_set != params_set: + raise ValueError( + f"Provided parameters {params_set} do not match Hamiltonian parameters {hamiltonian_param_set}." + ) self.hamiltonian = hamiltonian + if time <= 0: + raise ValueError(f"Time of evolution provided is not positive, detected time={time}.") self.time = time self.initial_state = initial_state self.aux_operators = aux_operators diff --git a/qiskit/algorithms/evolvers/real/trotterization/__init__.py b/qiskit/algorithms/evolvers/real/trotterization/__init__.py index 5bbe0f72c3e2..288a55a520a0 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/__init__.py +++ b/qiskit/algorithms/evolvers/real/trotterization/__init__.py @@ -10,13 +10,10 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """This package contains Trotterization-based Quantum Real Time Evolution algorithm. -It is compliant with the new Quantum Time Evolution Framework and makes use ProductFormula and -PauliEvolutionGate implementations. -The evolution with gradients assumes that a Hamiltonian is a linear combination of PauliOp objects -w.r.t. given parameters. It case of a single summand, it might be a PauliOp, or an OperatorBase. -Gradients are taken w.r.t. to a time parameter using the finite difference method (if -a Hamiltonian is time-dependent via t_param = Parameter("t")) and/or w.r.t. parameters present in a -Hamiltonian using an expectation value through a custom observable.""" +It is compliant with the new Quantum Time Evolution Framework and makes use of `ProductFormula` and +`PauliEvolutionGate` implementations. +The evolution with gradients assumes that a Hamiltonian is a linear combination of `PauliOp` objects +w.r.t. given parameters. It case of a single summand, it might be a `PauliOp`.""" from qiskit.algorithms.evolvers.real.trotterization.trotter_qrte import ( TrotterQRTE, diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index d9ef255d3939..5f3cda1788c4 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -23,6 +23,7 @@ CircuitOp, ExpectationBase, CircuitSampler, + PauliSumOp, ) from qiskit.circuit.library import PauliEvolutionGate from qiskit.providers import Backend, BaseBackend @@ -72,11 +73,12 @@ def __init__( if product_formula is None: product_formula = LieTrotter() self._product_formula = product_formula - self._quantum_instance = quantum_instance + self._quantum_instance = None + self._circuit_sampler = None + if quantum_instance is not None: + self.quantum_instance = quantum_instance self._expectation = expectation - self._circuit_sampler = CircuitSampler(quantum_instance) - @property def product_formula(self) -> ProductFormula: """Returns a product formula used in the algorithm.""" @@ -88,8 +90,21 @@ def quantum_instance(self) -> Union[QuantumInstance, BaseBackend, Backend]: return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend]): - """Sets a quantum instance used in the algorithm.""" + def quantum_instance( + self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] + ) -> None: + """Set quantum instance. + Args: + quantum_instance: The quantum instance used to run this algorithm. + """ + if isinstance(quantum_instance, (BaseBackend, Backend)): + quantum_instance = QuantumInstance(quantum_instance) + + if quantum_instance is not None: + self._circuit_sampler = CircuitSampler(quantum_instance) + else: + self._circuit_sampler = None + self._quantum_instance = quantum_instance @property @@ -134,6 +149,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: "`t_param` from the EvolutionProblem should be set to None." ) + # TODO handle no quantum_instance in aux_ops_evaluator; make it optional there if evolution_problem.aux_operators is not None and ( self._quantum_instance is None or self._expectation is None ): @@ -142,9 +158,11 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: "`quantum_instance` was provided." ) validate_hamiltonian_form(evolution_problem.hamiltonian) - hamiltonian = self._try_binding_params( - evolution_problem.hamiltonian, evolution_problem.hamiltonian_value_dict + hamiltonian = evolution_problem.hamiltonian.bind_parameters( + evolution_problem.hamiltonian_value_dict ) + if isinstance(hamiltonian, SummedOp): + hamiltonian = self._summed_op_to_pauli_sum_op(hamiltonian) # the evolution gate evolution_gate = CircuitOp( PauliEvolutionGate(hamiltonian, evolution_problem.time, synthesis=self._product_formula) @@ -152,7 +170,10 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: if evolution_problem.initial_state is not None: quantum_state = evolution_gate @ evolution_problem.initial_state - evolved_state = self._circuit_sampler.convert(quantum_state).eval() + if self._circuit_sampler is not None: + quantum_state = self._circuit_sampler.convert(quantum_state) + + evolved_state = quantum_state.eval() else: raise ValueError("`initial_state` must be provided in the EvolutionProblem.") @@ -167,45 +188,24 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: algorithm_globals.numerical_tolerance_at_0, ) - return EvolutionResult(evolved_state.eval(), evaluated_aux_ops) + return EvolutionResult(evolved_state, evaluated_aux_ops) @staticmethod - def _try_binding_params( - hamiltonian: Union[SummedOp, PauliOp], - hamiltonian_value_dict: Dict[Parameter, Union[float, complex]], - ) -> Union[SummedOp, PauliOp, OperatorBase]: + def _summed_op_to_pauli_sum_op( + hamiltonian: Union[SummedOp], + ) -> Union[PauliSumOp, PauliOp]: """ Tries binding parameters in a Hamiltonian. Args: - hamiltonian: The Hamiltonian of that defines an evolution. Only SummedOp, PauliOp are - supported by TrotterQrte. - hamiltonian_value_dict: Dictionary that maps all parameters in a Hamiltonian to - certain values. + hamiltonian: The Hamiltonian of that defines an evolution. Returns: - Bound Hamiltonian. - - Raises: - ValueError: If a Hamiltonian is not of an expected type. + Hamiltonian. """ # PauliSumOp does not allow parametrized coefficients but after binding the parameters # we need to convert it into a PauliSumOp for the PauliEvolutionGate. - if isinstance(hamiltonian, SummedOp): - op_list = [] - for op in hamiltonian.oplist: - if hamiltonian_value_dict is not None: - op_bound = op.bind_parameters(hamiltonian_value_dict) - else: - op_bound = op - is_op_bound(op_bound) - op_list.append(op_bound) - return sum(op_list) - elif isinstance(hamiltonian, PauliOp): # in case there is only a single summand - if hamiltonian_value_dict is not None: - op_bound = hamiltonian.bind_parameters(hamiltonian_value_dict) - else: - op_bound = hamiltonian - - is_op_bound(op_bound) - return op_bound + op_list = [] + for op in hamiltonian.oplist: + op_list.append(op) + return sum(op_list) diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index f164b8923a63..5c921198332b 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -52,19 +52,28 @@ def setUp(self): algorithm_globals.random_seed = self.seed shots = 1 backend = BasicAer.get_backend("statevector_simulator") + backend_qasm = BasicAer.get_backend("qasm_simulator") # TODO add to tests self.quantum_instance = QuantumInstance( backend=backend, shots=shots, seed_simulator=self.seed, seed_transpiler=self.seed, ) - - def test_trotter_qrte_trotter(self): + self.backends_dict = { + "qi": self.quantum_instance, + "b_sv": backend, + "b_qasm": backend_qasm, + "None": None, + } + + @data("qi", "b_sv", "None") + def test_trotter_qrte_trotter(self, quantum_instanc): """Test for trotter qrte.""" operator = SummedOp([X, Z]) # LieTrotter with 1 rep + quantum_instance = self.backends_dict[quantum_instanc] - trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) + trotter_qrte = TrotterQRTE(quantum_instance=quantum_instance) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -76,22 +85,28 @@ def test_trotter_qrte_trotter(self): np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - def test_trotter_qrte_trotter_aux_ops(self): + @data("qi", "b_sv") + def test_trotter_qrte_trotter_aux_ops(self, quantum_instance): """Test for trotter qrte.""" operator = SummedOp([X, Z]) # LieTrotter with 1 rep aux_ops = [X, Y] expectation = MatrixExpectation() - trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance, expectation=expectation) + quantum_instance = self.backends_dict[quantum_instance] + + trotter_qrte = TrotterQRTE(quantum_instance=quantum_instance, expectation=expectation) initial_state = Zero - evolution_problem = EvolutionProblem(operator, 1, initial_state, aux_ops) + time = 3 + evolution_problem = EvolutionProblem(operator, time, initial_state, aux_ops) evolution_result = trotter_qrte.evolve(evolution_problem) # Calculate the expected state expected_state = ( - expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() + expm(-time * 1j * Z.to_matrix()) + @ expm(-time * 1j * X.to_matrix()) + @ initial_state.to_matrix() ) expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - expected_aux_ops_evaluated = [(0.8268218104318058, 0.0), (0.3784012476539641, 0.0)] + expected_aux_ops_evaluated = [(0.078073, 0.0), (0.268286, 0.0)] np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) np.testing.assert_array_almost_equal( @@ -114,12 +129,14 @@ def test_trotter_qrte_trotter_2(self, operator): evolution_result = trotter_qrte.evolve(evolution_problem) np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - def test_trotter_qrte_suzuki(self): + @data("qi", "b_sv", "None") + def test_trotter_qrte_suzuki(self, quantum_instance): """Test for trotter qrte with Suzuki.""" operator = SummedOp([X, Z]) # 2nd order Suzuki with 1 rep + quantum_instance = self.backends_dict[quantum_instance] trotter_qrte = TrotterQRTE( - quantum_instance=self.quantum_instance, product_formula=SuzukiTrotter() + quantum_instance=quantum_instance, product_formula=SuzukiTrotter() ) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) @@ -135,12 +152,14 @@ def test_trotter_qrte_suzuki(self): np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - def test_trotter_qrte_qdrift(self): + @data("qi", "b_sv", "None") + def test_trotter_qrte_qdrift(self, quantum_instance): """Test for trotter qrte with QDrift.""" algorithm_globals.random_seed = 0 operator = SummedOp([X, Z]) + quantum_instance = self.backends_dict[quantum_instance] # QDrift with one repetition - trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance, product_formula=QDrift()) + trotter_qrte = TrotterQRTE(quantum_instance=quantum_instance, product_formula=QDrift()) initial_state = Zero evolution_problem = EvolutionProblem(operator, 1, initial_state) evolution_result = trotter_qrte.evolve(evolution_problem) @@ -160,8 +179,8 @@ def test_trotter_qrte_trotter_binding_missing_dict(self): operator = X * t_param + Z trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = Zero - evolution_problem = EvolutionProblem(operator, 1, initial_state, t_param=t_param) with assert_raises(ValueError): + evolution_problem = EvolutionProblem(operator, 1, initial_state, t_param=t_param) _ = trotter_qrte.evolve(evolution_problem) def test_trotter_qrte_trotter_binding_missing_param(self): @@ -170,8 +189,8 @@ def test_trotter_qrte_trotter_binding_missing_param(self): operator = X * t_param + Z trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = Zero - evolution_problem = EvolutionProblem(operator, 1, initial_state) with assert_raises(ValueError): + evolution_problem = EvolutionProblem(operator, 1, initial_state) _ = trotter_qrte.evolve(evolution_problem) From a72f018f426ead49f3c989f0b9d527e2426b56a3 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 23 Mar 2022 14:24:39 +0100 Subject: [PATCH 104/145] evolution_problem.py fix --- qiskit/algorithms/evolvers/evolution_problem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 078424249d97..ba40d9f50d68 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -57,7 +57,7 @@ def __init__( t_param_set.add(t_param) hamiltonian_dict_param_set = set() if hamiltonian_value_dict is not None: - hamiltonian_dict_param_set.add(hamiltonian_value_dict.keys()) + hamiltonian_dict_param_set.union(set(hamiltonian_value_dict.keys())) params_set = t_param_set.union(hamiltonian_dict_param_set) hamiltonian_param_set = set(hamiltonian.parameters) if hamiltonian_param_set != params_set: From e98f5b72105650fd26c0aa2e5d456aeaf2a3997c Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 23 Mar 2022 16:04:28 +0100 Subject: [PATCH 105/145] Removed trotter_ops_validator.py for now, extended unit tests, code refactoring. --- .../algorithms/evolvers/evolution_problem.py | 43 ++++-- .../trotterization/trotter_ops_validator.py | 132 ------------------ .../real/trotterization/trotter_qrte.py | 27 ++-- .../test_trotter_ops_validator.py | 82 ----------- .../real/trotterization/test_trotter_qrte.py | 17 ++- 5 files changed, 61 insertions(+), 240 deletions(-) delete mode 100644 qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py delete mode 100644 test/python/algorithms/evolvers/real/trotterization/test_trotter_ops_validator.py diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index ba40d9f50d68..4b6bdeddb8fc 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -18,6 +18,7 @@ from qiskit.circuit import Parameter from qiskit.opflow import OperatorBase, StateFn from ..list_or_dict import ListOrDict +from ...quantum_info import SparsePauliOp class EvolutionProblem: @@ -52,6 +53,37 @@ def __init__( ValueError: If not all parameter values are provided. ValueError: If non-positive time of evolution is provided. """ + + if not isinstance(hamiltonian, SparsePauliOp): + self._check_parameters(hamiltonian, hamiltonian_value_dict, t_param) + self.hamiltonian = hamiltonian + if time <= 0: + raise ValueError(f"Time of evolution provided is not positive, detected time={time}.") + self.time = time + self.initial_state = initial_state + self.aux_operators = aux_operators + self.t_param = t_param + self.hamiltonian_value_dict = hamiltonian_value_dict + + def _check_parameters( + self, + hamiltonian: OperatorBase, + hamiltonian_value_dict: Optional[Dict[Parameter, Union[complex]]] = None, + t_param: Optional[Parameter] = None, + ) -> None: + """ + Checks if all parameters present in the Hamiltonian are also present in the dictionary + that maps them to values. + Args: + hamiltonian: The Hamiltonian under which to evolve the system. + hamiltonian_value_dict: If the Hamiltonian contains free parameters, this + dictionary maps all these parameters to values. + t_param: Time parameter in case of a time-dependent Hamiltonian. This + free parameter must be within the ``hamiltonian``. + + Raises: + ValueError: If there are unbound parameters in the Hamiltonian. + """ t_param_set = set() if t_param is not None: t_param_set.add(t_param) @@ -62,13 +94,6 @@ def __init__( hamiltonian_param_set = set(hamiltonian.parameters) if hamiltonian_param_set != params_set: raise ValueError( - f"Provided parameters {params_set} do not match Hamiltonian parameters {hamiltonian_param_set}." + f"Provided parameters {params_set} do not match Hamiltonian parameters " + f"{hamiltonian_param_set}." ) - self.hamiltonian = hamiltonian - if time <= 0: - raise ValueError(f"Time of evolution provided is not positive, detected time={time}.") - self.time = time - self.initial_state = initial_state - self.aux_operators = aux_operators - self.t_param = t_param - self.hamiltonian_value_dict = hamiltonian_value_dict diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py deleted file mode 100644 index 73d21453ea2d..000000000000 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_ops_validator.py +++ /dev/null @@ -1,132 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. - -"""Set of method for validating input to TrotterQrte algorithm.""" - -import numbers -from typing import Union - -from qiskit.circuit import Parameter, ParameterExpression -from qiskit.opflow import ( - SummedOp, - PauliOp, -) - - -def is_op_bound(operator: Union[SummedOp, PauliOp]) -> None: - """Checks if an operator provided has all parameters bound. - - Args: - operator: Operator to be checked. - - Raises: - ValueError: If an operator has unbound parameters. - """ - if len(operator.parameters) != 0: - raise ValueError( - f"Did not manage to bind all parameters in the Hamiltonian, " - f"these parameters encountered: {operator.parameters}." - ) - - -def validate_hamiltonian_form(hamiltonian: Union[SummedOp, PauliOp]): - """Validates that a Hamiltonian is of a correct type and with expected dependence on - parameters. - - Args: - hamiltonian: Hamiltonian to be validated. - - Raises: - ValueError: if an invalid Hamiltonian is provided. - """ - value_error = ValueError( - "Hamiltonian term has a coefficient that is not a linear function of a " - "single parameter. It is not supported." - ) - if isinstance(hamiltonian, SummedOp): - if isinstance(hamiltonian.coeff, ParameterExpression): - raise ValueError( - f"The coefficient multiplying the whole Hamiltonian cannot be a " - f"ParameterExpression. The following coefficient was detected: {hamiltonian.coeff}." - ) - for op in hamiltonian.oplist: - if not _is_pauli_lin_single_param(op): - raise value_error - elif isinstance(hamiltonian, PauliOp): - if not _is_pauli_lin_single_param(hamiltonian): - raise value_error - else: - raise ValueError( - f"Hamiltonian not a SummedOp/PauliOp which are the only options supported. The " - f"following type detected instead: {type(hamiltonian)}." - ) - - -def _is_pauli_lin_single_param(operator: PauliOp) -> bool: - """Checks if an operator provided is linear w.r.t. one and only one parameter. - - Args: - operator: Operator to be checked. - - Returns: - True or False depending on whether an operator is linear in a single param and only contains - a single param. - - Raises: - ValueError: If an operator contains more than 1 parameter. - """ - if not isinstance(operator, PauliOp): - raise ValueError(f"Only PauliOp expected. {type(operator)} provided") - if _is_operator_parametrized(operator): - return True - if len(operator.coeff.parameters) > 1: - raise ValueError( - "Term of a Hamiltonian has a coefficient that depends on several " - "parameters. Only dependence on a single parameter is allowed." - ) - gradient = _operator_derivative(operator) - return isinstance(gradient, numbers.Number) - - -def _operator_derivative( - operator: PauliOp, -) -> Union[numbers.Number, Parameter, ParameterExpression]: - """ - Calculates the gradient of an operator coefficient. - - Args: - operator: PauliOp. - - Returns: - Gradient of a PauliOp coefficient. - """ - single_parameter_expression = operator.coeff - parameter = list(single_parameter_expression.parameters)[0] - gradient = single_parameter_expression.gradient(parameter) - return gradient - - -def _is_operator_parametrized(operator: PauliOp) -> bool: - """ - Checks if an operator is parametrized. - - Args: - operator: PauliOp. - - Returns: - Boolean flag indicating whether the PauliOp is parametrized or not. - """ - return ( - not isinstance(operator.coeff, Parameter) - if not isinstance(operator.coeff, ParameterExpression) - else len(operator.coeff.parameters) == 0 - ) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 5f3cda1788c4..f814ff01b07c 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -12,12 +12,10 @@ """An algorithm to implement a Trotterization real time-evolution.""" -from typing import Union, Dict, Optional +from typing import Union, Optional from qiskit.algorithms import EvolutionProblem, EvolutionResult, RealEvolver, eval_observables -from qiskit.circuit import Parameter from qiskit.opflow import ( - OperatorBase, SummedOp, PauliOp, CircuitOp, @@ -27,9 +25,9 @@ ) from qiskit.circuit.library import PauliEvolutionGate from qiskit.providers import Backend, BaseBackend +from qiskit.quantum_info import SparsePauliOp, Pauli from qiskit.synthesis import ProductFormula, LieTrotter from qiskit.utils import QuantumInstance, algorithm_globals -from .trotter_ops_validator import is_op_bound, validate_hamiltonian_form class TrotterQRTE(RealEvolver): @@ -100,10 +98,9 @@ def quantum_instance( if isinstance(quantum_instance, (BaseBackend, Backend)): quantum_instance = QuantumInstance(quantum_instance) + self._circuit_sampler = None if quantum_instance is not None: self._circuit_sampler = CircuitSampler(quantum_instance) - else: - self._circuit_sampler = None self._quantum_instance = quantum_instance @@ -133,7 +130,8 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: Args: evolution_problem: Instance defining evolution problem. For the included Hamiltonian, - only SummedOp, PauliOp are supported by TrotterQrte. + `PauliOp`, `SparsePauliOp`, `Pauli` or `SummedOp` thereof or `PauliSumOp` are + supported by TrotterQrte. Returns: Evolution result that includes an evolved state. @@ -157,10 +155,9 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: "aux_operators where provided for evaluations but no `expectation` or " "`quantum_instance` was provided." ) - validate_hamiltonian_form(evolution_problem.hamiltonian) - hamiltonian = evolution_problem.hamiltonian.bind_parameters( - evolution_problem.hamiltonian_value_dict - ) + hamiltonian = evolution_problem.hamiltonian + if not isinstance(hamiltonian, SparsePauliOp): # TODO can we handle it better? + hamiltonian = hamiltonian.bind_parameters(evolution_problem.hamiltonian_value_dict) if isinstance(hamiltonian, SummedOp): hamiltonian = self._summed_op_to_pauli_sum_op(hamiltonian) # the evolution gate @@ -202,10 +199,18 @@ def _summed_op_to_pauli_sum_op( Returns: Hamiltonian. + + Raises: + ValueError: If the `SummedOp` Hamiltonian contains operators of an invalid type. """ # PauliSumOp does not allow parametrized coefficients but after binding the parameters # we need to convert it into a PauliSumOp for the PauliEvolutionGate. op_list = [] for op in hamiltonian.oplist: + if not isinstance(op, (PauliOp, Pauli, SparsePauliOp)): + raise ValueError( + f"Content of the Hamiltonian not of type PauliOp, Pauli, or SparsePauliOp. The " + f"following type detected: {type(op)}." + ) op_list.append(op) return sum(op_list) diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_ops_validator.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_ops_validator.py deleted file mode 100644 index 5779893d0d02..000000000000 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_ops_validator.py +++ /dev/null @@ -1,82 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. - -"""Tests QRTE operators validation methods.""" - -import unittest - -from test.python.opflow import QiskitOpflowTestCase -from ddt import ddt, data, unpack -import numpy as np - -from qiskit.algorithms.evolvers.real.trotterization.trotter_ops_validator import ( - validate_hamiltonian_form, - _is_pauli_lin_single_param, -) -from qiskit.circuit.library import EfficientSU2 -from qiskit.circuit import Parameter -from qiskit.opflow import ( - X, - Z, - Y, -) - - -@ddt -class TestTrotterQrte(QiskitOpflowTestCase): - """Trotter QRTE operators validation tests.""" - - @data( - (X, True), - (Parameter("theta") * Y, True), - (Parameter("theta") * Parameter("gamma") * Z, False), - (Parameter("theta") * X + Parameter("gamma") * Y, True), - (Parameter("theta") * X + Y, True), - (X + Parameter("gamma") * Y, True), - (Parameter("theta1") * Parameter("theta2") * X + Parameter("gamma") * Y, False), - (EfficientSU2, False), - ) - @unpack - def test_validate_hamiltonian_form(self, hamiltonian, expected): - """Tests that a Hamiltonian is of a valid form supported by the TrotterQrte algorithm.""" - valid = True - try: - validate_hamiltonian_form(hamiltonian) - except ValueError: - valid = False - - np.testing.assert_equal(valid, expected) - - @data( - (X, True), - (X + Y, False), - (-5 * X, True), - (5j * Y, True), - (X + Parameter("theta1") * Parameter("theta2") * Y, False), - (Parameter("theta") * Y, True), - (Parameter("theta") * Parameter("gamma") * Z, False), - (5 * Parameter("theta") * X, True), - (Parameter("theta1") * Parameter("theta2") * X, False), - ) - @unpack - def test_is_pauli_lin_single_param(self, operator, expected): - """Tests that operator is validated to be linearly dependent on a single parameter.""" - try: - linear_with_single_param = _is_pauli_lin_single_param(operator) - except ValueError: - linear_with_single_param = False - - np.testing.assert_equal(linear_with_single_param, expected) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index 5c921198332b..811b6a3e9830 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -14,18 +14,18 @@ import unittest -from test.python.opflow import QiskitOpflowTestCase from ddt import ddt, data import numpy as np from numpy.testing import assert_raises from scipy.linalg import expm +from test.python.opflow import QiskitOpflowTestCase from qiskit import BasicAer from qiskit.algorithms import EvolutionProblem from qiskit.algorithms.evolvers.real.trotterization.trotter_qrte import ( TrotterQRTE, ) -from qiskit.quantum_info import Statevector +from qiskit.quantum_info import Statevector, SparsePauliOp, Pauli, PauliTable from qiskit.utils import algorithm_globals, QuantumInstance from qiskit.circuit import Parameter from qiskit.opflow import ( @@ -113,13 +113,18 @@ def test_trotter_qrte_trotter_aux_ops(self, quantum_instance): evolution_result.aux_ops_evaluated, expected_aux_ops_evaluated ) - @data(SummedOp([(X ^ Y), (Y ^ X)]), SummedOp([(Z ^ Z), (Z ^ I), (I ^ Z)]), Y ^ Y) + @data( + SummedOp([(X ^ Y), (Y ^ X)]), + (Z ^ Z) + (Z ^ I) + (I ^ Z), + Y ^ Y, + SparsePauliOp(Pauli("XI")), + SparsePauliOp(PauliTable.from_labels(["XX", "ZZ"])), + ) def test_trotter_qrte_trotter_2(self, operator): - """Test for trotter qrte.""" + """Test for trotter qrte with various types of a Hamiltonian.""" # LieTrotter with 1 rep trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = StateFn([1, 0, 0, 0]) - # Calculate the expected state expected_state = initial_state.to_matrix() expected_state = expm(-1j * operator.to_matrix()) @ expected_state @@ -132,7 +137,7 @@ def test_trotter_qrte_trotter_2(self, operator): @data("qi", "b_sv", "None") def test_trotter_qrte_suzuki(self, quantum_instance): """Test for trotter qrte with Suzuki.""" - operator = SummedOp([X, Z]) + operator = X + Z # 2nd order Suzuki with 1 rep quantum_instance = self.backends_dict[quantum_instance] trotter_qrte = TrotterQRTE( From af1c1dfe005256fb6db5d81a91d5011c16fd0ea5 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 24 Mar 2022 10:09:41 +0100 Subject: [PATCH 106/145] Updated names of the algorithm. --- qiskit/algorithms/__init__.py | 2 +- .../evolvers/real/trotterization/trotter_qrte.py | 8 ++++---- .../feature-trotter-qrte-f7b28c4fd4b361d2.yaml | 2 +- .../real/trotterization/test_trotter_qrte.py | 16 ++++++++-------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index 0d71a3678dd4..b6908c1c0ef7 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -106,7 +106,7 @@ RealEvolver ImaginaryEvolver - TrotterQrte + TrotterQRTE EvolutionResult EvolutionProblem diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index f814ff01b07c..ccc3619916b0 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -130,8 +130,8 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: Args: evolution_problem: Instance defining evolution problem. For the included Hamiltonian, - `PauliOp`, `SparsePauliOp`, `Pauli` or `SummedOp` thereof or `PauliSumOp` are - supported by TrotterQrte. + ``PauliOp``, ``SparsePauliOp``, ``Pauli`` or ``SummedOp`` thereof or ``PauliSumOp`` + are supported by TrotterQRTE. Returns: Evolution result that includes an evolved state. @@ -143,8 +143,8 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: """ if evolution_problem.t_param is not None: raise ValueError( - "TrotterQrte does not accept a time dependent hamiltonian," - "`t_param` from the EvolutionProblem should be set to None." + "TrotterQRTE does not accept a time dependent hamiltonian," + "``t_param`` from the EvolutionProblem should be set to None." ) # TODO handle no quantum_instance in aux_ops_evaluator; make it optional there diff --git a/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml b/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml index 8f26f5d1bc6f..00e344c66017 100644 --- a/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml +++ b/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml @@ -2,6 +2,6 @@ features: - | Added Trotterization-based Quantum Real Time Evolution Algorithm - :class:`qiskit.algorithms.TrotterQrte`. It is compliant with the new Quantum Time Evolution + :class:`qiskit.algorithms.TrotterQRTE`. It is compliant with the new Quantum Time Evolution Framework and makes use of :class:`qiskit.synthesis.evolution.ProductFormula` and :class:`qiskit.circuit.library.PauliEvolutionGate` implementations. diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index 811b6a3e9830..fc5acc398e75 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" Test Trotter Qrte. """ +""" Test TrotterQRTE. """ import unittest @@ -44,7 +44,7 @@ @ddt class TestTrotterQRTE(QiskitOpflowTestCase): - """Trotter Qrte tests.""" + """TrotterQRTE tests.""" def setUp(self): super().setUp() @@ -68,7 +68,7 @@ def setUp(self): @data("qi", "b_sv", "None") def test_trotter_qrte_trotter(self, quantum_instanc): - """Test for trotter qrte.""" + """Test for TrotterQRTE.""" operator = SummedOp([X, Z]) # LieTrotter with 1 rep quantum_instance = self.backends_dict[quantum_instanc] @@ -87,7 +87,7 @@ def test_trotter_qrte_trotter(self, quantum_instanc): @data("qi", "b_sv") def test_trotter_qrte_trotter_aux_ops(self, quantum_instance): - """Test for trotter qrte.""" + """Test for TrotterQRTE.""" operator = SummedOp([X, Z]) # LieTrotter with 1 rep aux_ops = [X, Y] @@ -136,7 +136,7 @@ def test_trotter_qrte_trotter_2(self, operator): @data("qi", "b_sv", "None") def test_trotter_qrte_suzuki(self, quantum_instance): - """Test for trotter qrte with Suzuki.""" + """Test for TrotterQRTE with Suzuki.""" operator = X + Z # 2nd order Suzuki with 1 rep quantum_instance = self.backends_dict[quantum_instance] @@ -159,7 +159,7 @@ def test_trotter_qrte_suzuki(self, quantum_instance): @data("qi", "b_sv", "None") def test_trotter_qrte_qdrift(self, quantum_instance): - """Test for trotter qrte with QDrift.""" + """Test for TrotterQRTE with QDrift.""" algorithm_globals.random_seed = 0 operator = SummedOp([X, Z]) quantum_instance = self.backends_dict[quantum_instance] @@ -179,7 +179,7 @@ def test_trotter_qrte_qdrift(self, quantum_instance): np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) def test_trotter_qrte_trotter_binding_missing_dict(self): - """Test for trotter qrte with binding and missing dictionary..""" + """Test for TrotterQRTE with binding and missing dictionary..""" t_param = Parameter("t") operator = X * t_param + Z trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) @@ -189,7 +189,7 @@ def test_trotter_qrte_trotter_binding_missing_dict(self): _ = trotter_qrte.evolve(evolution_problem) def test_trotter_qrte_trotter_binding_missing_param(self): - """Test for trotter qrte with binding and missing param.""" + """Test for TrotterQRTE with binding and missing param.""" t_param = Parameter("t") operator = X * t_param + Z trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) From 6200d9b7281a150a0561208eb49ee1d88d0c0779 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 24 Mar 2022 10:10:29 +0100 Subject: [PATCH 107/145] Added support for QuantumCircuit input. Code refactoring. --- .../algorithms/evolvers/evolution_problem.py | 12 +++++--- .../real/trotterization/trotter_qrte.py | 23 +++++++++------ .../real/trotterization/test_trotter_qrte.py | 28 +++++++++++++++++-- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 4b6bdeddb8fc..8a56d266eca1 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -50,15 +50,19 @@ def __init__( dictionary maps all these parameters to values. Raises: - ValueError: If not all parameter values are provided. ValueError: If non-positive time of evolution is provided. + ValueError: If no ``initial_state`` is provided. + ValueError: If not all parameter values are provided. """ - + if time <= 0: + raise ValueError(f"Time of evolution provided is not positive, detected time={time}.") + if initial_state is None: + raise ValueError("No initial_state provided for the EvolutionProblem. It is required.") + # TODO SparsePauliOp does not have .parameters because it is not allowed to be parametrized. + # Can we handle this better than with an if? if not isinstance(hamiltonian, SparsePauliOp): self._check_parameters(hamiltonian, hamiltonian_value_dict, t_param) self.hamiltonian = hamiltonian - if time <= 0: - raise ValueError(f"Time of evolution provided is not positive, detected time={time}.") self.time = time self.initial_state = initial_state self.aux_operators = aux_operators diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index ccc3619916b0..fa95f74b1efe 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -14,6 +14,7 @@ from typing import Union, Optional +from qiskit import QuantumCircuit from qiskit.algorithms import EvolutionProblem, EvolutionResult, RealEvolver, eval_observables from qiskit.opflow import ( SummedOp, @@ -22,6 +23,7 @@ ExpectationBase, CircuitSampler, PauliSumOp, + StateFn, ) from qiskit.circuit.library import PauliEvolutionGate from qiskit.providers import Backend, BaseBackend @@ -91,7 +93,7 @@ def quantum_instance(self) -> Union[QuantumInstance, BaseBackend, Backend]: def quantum_instance( self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] ) -> None: - """Set quantum instance. + """Sets a quantum instance and a circuit sampler. Args: quantum_instance: The quantum instance used to run this algorithm. """ @@ -115,7 +117,7 @@ def supports_aux_operators(cls) -> bool: Whether computing the expectation value of auxiliary operators is supported. Returns: - True if `aux_operators` expectations in the EvolutionProblem can be evaluated, False + True if ``aux_operators`` expectations in the EvolutionProblem can be evaluated, False otherwise. """ return True @@ -137,9 +139,9 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: Evolution result that includes an evolved state. Raises: - ValueError: If `t_param` is not set to None in the EvolutionProblem (feature not + ValueError: If ``t_param`` is not set to None in the EvolutionProblem (feature not currently supported). - ValueError: If the `initial_state` is not provided in the EvolutionProblem. + ValueError: If the ``initial_state`` is not provided in the EvolutionProblem. """ if evolution_problem.t_param is not None: raise ValueError( @@ -152,8 +154,8 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: self._quantum_instance is None or self._expectation is None ): raise ValueError( - "aux_operators where provided for evaluations but no `expectation` or " - "`quantum_instance` was provided." + "aux_operators where provided for evaluations but no ``expectation`` or " + "``quantum_instance`` was provided." ) hamiltonian = evolution_problem.hamiltonian if not isinstance(hamiltonian, SparsePauliOp): # TODO can we handle it better? @@ -166,14 +168,17 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: ) if evolution_problem.initial_state is not None: - quantum_state = evolution_gate @ evolution_problem.initial_state + initial_state = evolution_problem.initial_state + if isinstance(initial_state, QuantumCircuit): + initial_state = StateFn(initial_state) + quantum_state = evolution_gate @ initial_state if self._circuit_sampler is not None: quantum_state = self._circuit_sampler.convert(quantum_state) evolved_state = quantum_state.eval() else: - raise ValueError("`initial_state` must be provided in the EvolutionProblem.") + raise ValueError("``initial_state`` must be provided in the EvolutionProblem.") evaluated_aux_ops = None if evolution_problem.aux_operators is not None: @@ -201,7 +206,7 @@ def _summed_op_to_pauli_sum_op( Hamiltonian. Raises: - ValueError: If the `SummedOp` Hamiltonian contains operators of an invalid type. + ValueError: If the ``SummedOp`` Hamiltonian contains operators of an invalid type. """ # PauliSumOp does not allow parametrized coefficients but after binding the parameters # we need to convert it into a PauliSumOp for the PauliEvolutionGate. diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index fc5acc398e75..19c400d61dfa 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -14,13 +14,13 @@ import unittest +from test.python.opflow import QiskitOpflowTestCase from ddt import ddt, data import numpy as np from numpy.testing import assert_raises from scipy.linalg import expm -from test.python.opflow import QiskitOpflowTestCase -from qiskit import BasicAer +from qiskit import BasicAer, QuantumCircuit from qiskit.algorithms import EvolutionProblem from qiskit.algorithms.evolvers.real.trotterization.trotter_qrte import ( TrotterQRTE, @@ -121,7 +121,7 @@ def test_trotter_qrte_trotter_aux_ops(self, quantum_instance): SparsePauliOp(PauliTable.from_labels(["XX", "ZZ"])), ) def test_trotter_qrte_trotter_2(self, operator): - """Test for trotter qrte with various types of a Hamiltonian.""" + """Test for TrotterQRTE with various types of a Hamiltonian.""" # LieTrotter with 1 rep trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = StateFn([1, 0, 0, 0]) @@ -198,6 +198,28 @@ def test_trotter_qrte_trotter_binding_missing_param(self): evolution_problem = EvolutionProblem(operator, 1, initial_state) _ = trotter_qrte.evolve(evolution_problem) + @data("qi", "b_sv", "None") + def test_trotter_qrte_qdrift_circuit(self, quantum_instance): + """Test for TrotterQRTE with QDrift.""" + algorithm_globals.random_seed = 0 + operator = SummedOp([X, Z]) + quantum_instance = self.backends_dict[quantum_instance] + # QDrift with one repetition + trotter_qrte = TrotterQRTE(quantum_instance=quantum_instance, product_formula=QDrift()) + initial_state = QuantumCircuit(1) + initial_state.append(X, [0]) + evolution_problem = EvolutionProblem(operator, 1, initial_state) + evolution_result = trotter_qrte.evolve(evolution_problem) + sampled_ops = [Z, X, X, X, Z, Z, Z, Z] + evo_time = 0.25 + # Calculate the expected state + expected_state = StateFn(initial_state).to_matrix() + for op in sampled_ops: + expected_state = expm(-1j * op.to_matrix() * evo_time) @ expected_state + expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) + + np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) + if __name__ == "__main__": unittest.main() From 60284c23b94abf74cd263b3e90f78611ce711af0 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 24 Mar 2022 13:54:14 +0100 Subject: [PATCH 108/145] Improved error handling in evolution_problem.py and unit tests added. --- qiskit/algorithms/evolvers/evolution_problem.py | 12 ++++++++++-- .../algorithms/evolvers/test_evolution_problem.py | 13 ++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 8a56d266eca1..682bd315c231 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -55,9 +55,17 @@ def __init__( ValueError: If not all parameter values are provided. """ if time <= 0: - raise ValueError(f"Time of evolution provided is not positive, detected time={time}.") + raise ValueError( + f"Time of evolution provided is not positive, detected " f"``time={time}``." + ) if initial_state is None: - raise ValueError("No initial_state provided for the EvolutionProblem. It is required.") + raise ValueError( + "No ``initial_state`` provided for the EvolutionProblem. It is " "required." + ) + if hamiltonian is None: + raise ValueError( + "No ``hamiltonian`` provided for the EvolutionProblem. It is " "required." + ) # TODO SparsePauliOp does not have .parameters because it is not allowed to be parametrized. # Can we handle this better than with an if? if not isinstance(hamiltonian, SparsePauliOp): diff --git a/test/python/algorithms/evolvers/test_evolution_problem.py b/test/python/algorithms/evolvers/test_evolution_problem.py index cdd472247045..c9e6d4c0a53a 100644 --- a/test/python/algorithms/evolvers/test_evolution_problem.py +++ b/test/python/algorithms/evolvers/test_evolution_problem.py @@ -13,12 +13,16 @@ """Test evolver problem class.""" import unittest +from ddt import data, ddt, unpack +from numpy.testing import assert_raises + from test.python.algorithms import QiskitAlgorithmsTestCase from qiskit.algorithms.evolvers.evolution_problem import EvolutionProblem from qiskit.circuit import Parameter -from qiskit.opflow import Y, Z, One, X +from qiskit.opflow import Y, Z, One, X, Zero +@ddt class TestEvolutionProblem(QiskitAlgorithmsTestCase): """Test evolver problem class.""" @@ -71,6 +75,13 @@ def test_init_all(self): self.assertEqual(evo_problem.t_param, expected_t_param) self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) + @data([Y, -1, One], [Y, -1.2, One], [Y, 0, One], [None, 1, Zero], [Y, 2.5, None]) + @unpack + def test_init_errors(self, hamiltonian, time, initial_state): + """Tests that all default fields are initialized correctly.""" + with assert_raises(ValueError): + _ = EvolutionProblem(hamiltonian, time, initial_state) + if __name__ == "__main__": unittest.main() From 6f37d47b6ca3bd397bcc3261de96bd069d374334 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 24 Mar 2022 15:37:53 +0100 Subject: [PATCH 109/145] Code refactoring. --- .../real/trotterization/trotter_qrte.py | 6 +- .../real/trotterization/test_trotter_qrte.py | 77 ++++++++++--------- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index fa95f74b1efe..8eef048f6cf4 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -68,7 +68,10 @@ def __init__( first order product formula with a single repetition. expectation: An instance of ExpectationBase which defines a method for calculating expectation values of EvolutionProblem.aux_operators. - quantum_instance: A quantum instance used for calculations. + quantum_instance: A quantum instance used for calculations. If not provided, + calculations are performed clasically which work reasonably only for small systems. + In case of auxiliary operators provided in ``EvolutionProblem``, a quantum instance + is required. """ if product_formula is None: product_formula = LieTrotter() @@ -149,7 +152,6 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: "``t_param`` from the EvolutionProblem should be set to None." ) - # TODO handle no quantum_instance in aux_ops_evaluator; make it optional there if evolution_problem.aux_operators is not None and ( self._quantum_instance is None or self._expectation is None ): diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index 19c400d61dfa..a508e0c21fff 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -50,25 +50,30 @@ def setUp(self): super().setUp() self.seed = 50 algorithm_globals.random_seed = self.seed - shots = 1 backend = BasicAer.get_backend("statevector_simulator") backend_qasm = BasicAer.get_backend("qasm_simulator") # TODO add to tests self.quantum_instance = QuantumInstance( backend=backend, - shots=shots, + shots=1, + seed_simulator=self.seed, + seed_transpiler=self.seed, + ) + self.quantum_instance_qasm = QuantumInstance( + backend=backend_qasm, + shots=4000, seed_simulator=self.seed, seed_transpiler=self.seed, ) self.backends_dict = { - "qi": self.quantum_instance, + "qi_sv": self.quantum_instance, + "qi_qasm": self.quantum_instance_qasm, "b_sv": backend, - "b_qasm": backend_qasm, "None": None, } - @data("qi", "b_sv", "None") - def test_trotter_qrte_trotter(self, quantum_instanc): - """Test for TrotterQRTE.""" + @data("qi_sv", "b_sv", "None") + def test_trotter_qrte_trotter_single_qubit(self, quantum_instanc): + """Test for default TrotterQRTE on a single qubit.""" operator = SummedOp([X, Z]) # LieTrotter with 1 rep quantum_instance = self.backends_dict[quantum_instanc] @@ -85,9 +90,9 @@ def test_trotter_qrte_trotter(self, quantum_instanc): np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - @data("qi", "b_sv") - def test_trotter_qrte_trotter_aux_ops(self, quantum_instance): - """Test for TrotterQRTE.""" + @data("qi_sv", "b_sv") + def test_trotter_qrte_trotter_single_qubit_aux_ops(self, quantum_instance): + """Test for default TrotterQRTE on a single qubit with auxiliary operators.""" operator = SummedOp([X, Z]) # LieTrotter with 1 rep aux_ops = [X, Y] @@ -120,8 +125,8 @@ def test_trotter_qrte_trotter_aux_ops(self, quantum_instance): SparsePauliOp(Pauli("XI")), SparsePauliOp(PauliTable.from_labels(["XX", "ZZ"])), ) - def test_trotter_qrte_trotter_2(self, operator): - """Test for TrotterQRTE with various types of a Hamiltonian.""" + def test_trotter_qrte_trotter_two_qubits(self, operator): + """Test for TrotterQRTE on two qubits with various types of a Hamiltonian.""" # LieTrotter with 1 rep trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = StateFn([1, 0, 0, 0]) @@ -134,7 +139,7 @@ def test_trotter_qrte_trotter_2(self, operator): evolution_result = trotter_qrte.evolve(evolution_problem) np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - @data("qi", "b_sv", "None") + @data("qi_sv", "b_sv", "None") def test_trotter_qrte_suzuki(self, quantum_instance): """Test for TrotterQRTE with Suzuki.""" operator = X + Z @@ -157,8 +162,8 @@ def test_trotter_qrte_suzuki(self, quantum_instance): np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - @data("qi", "b_sv", "None") - def test_trotter_qrte_qdrift(self, quantum_instance): + @data("qi_sv", "b_sv", "None") + def test_trotter_qrte_qdrift_fractional_time(self, quantum_instance): """Test for TrotterQRTE with QDrift.""" algorithm_globals.random_seed = 0 operator = SummedOp([X, Z]) @@ -178,27 +183,7 @@ def test_trotter_qrte_qdrift(self, quantum_instance): np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - def test_trotter_qrte_trotter_binding_missing_dict(self): - """Test for TrotterQRTE with binding and missing dictionary..""" - t_param = Parameter("t") - operator = X * t_param + Z - trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) - initial_state = Zero - with assert_raises(ValueError): - evolution_problem = EvolutionProblem(operator, 1, initial_state, t_param=t_param) - _ = trotter_qrte.evolve(evolution_problem) - - def test_trotter_qrte_trotter_binding_missing_param(self): - """Test for TrotterQRTE with binding and missing param.""" - t_param = Parameter("t") - operator = X * t_param + Z - trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) - initial_state = Zero - with assert_raises(ValueError): - evolution_problem = EvolutionProblem(operator, 1, initial_state) - _ = trotter_qrte.evolve(evolution_problem) - - @data("qi", "b_sv", "None") + @data("qi_sv", "b_sv", "None") def test_trotter_qrte_qdrift_circuit(self, quantum_instance): """Test for TrotterQRTE with QDrift.""" algorithm_globals.random_seed = 0 @@ -220,6 +205,26 @@ def test_trotter_qrte_qdrift_circuit(self, quantum_instance): np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) + def test_trotter_qrte_trotter_binding_missing_dict(self): + """Test for TrotterQRTE with binding and missing dictionary..""" + t_param = Parameter("t") + operator = X * t_param + Z + trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) + initial_state = Zero + with assert_raises(ValueError): + evolution_problem = EvolutionProblem(operator, 1, initial_state, t_param=t_param) + _ = trotter_qrte.evolve(evolution_problem) + + def test_trotter_qrte_trotter_binding_missing_param(self): + """Test for TrotterQRTE with binding and missing param.""" + t_param = Parameter("t") + operator = X * t_param + Z + trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) + initial_state = Zero + with assert_raises(ValueError): + evolution_problem = EvolutionProblem(operator, 1, initial_state) + _ = trotter_qrte.evolve(evolution_problem) + if __name__ == "__main__": unittest.main() From 213a6172597d31572c7134000d229aed57a17a78 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 24 Mar 2022 17:26:42 +0100 Subject: [PATCH 110/145] Cyclic import fix --- .../algorithms/evolvers/real/trotterization/trotter_qrte.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 8eef048f6cf4..dfa40917c70b 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -15,7 +15,9 @@ from typing import Union, Optional from qiskit import QuantumCircuit -from qiskit.algorithms import EvolutionProblem, EvolutionResult, RealEvolver, eval_observables +from qiskit.algorithms.aux_ops_evaluator import eval_observables +from qiskit.algorithms.evolvers import EvolutionProblem, EvolutionResult +from qiskit.algorithms.evolvers.real.real_evolver import RealEvolver from qiskit.opflow import ( SummedOp, PauliOp, From dfe2df45c56804d783a96fad8724a3bb673f381a Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 25 Mar 2022 11:45:10 +0100 Subject: [PATCH 111/145] Refactored evolution_problem.py --- .../algorithms/evolvers/evolution_problem.py | 87 +++++++++++++++---- .../evolvers/test_evolution_problem.py | 2 +- 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 682bd315c231..1eac67d3f166 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -54,28 +54,79 @@ def __init__( ValueError: If no ``initial_state`` is provided. ValueError: If not all parameter values are provided. """ - if time <= 0: - raise ValueError( - f"Time of evolution provided is not positive, detected " f"``time={time}``." - ) - if initial_state is None: - raise ValueError( - "No ``initial_state`` provided for the EvolutionProblem. It is " "required." - ) + + self.t_param = t_param + self.hamiltonian_value_dict = hamiltonian_value_dict + self.hamiltonian = hamiltonian + self.time = time + self.initial_state = initial_state + self.aux_operators = aux_operators + + @property + def hamiltonian(self) -> OperatorBase: + """Returns a hamiltonian.""" + return self._hamiltonian + + @hamiltonian.setter + def hamiltonian(self, hamiltonian) -> None: + """ + Sets a hamiltonian and validates it. + + Raises: + ValueError: If no Hamiltonian is provided. + ValueError: If Hamiltonian parameters cannot be bound with data provided. + """ + self._hamiltonian = None if hamiltonian is None: raise ValueError( - "No ``hamiltonian`` provided for the EvolutionProblem. It is " "required." + "No ``hamiltonian`` provided for the EvolutionProblem. It is required." ) # TODO SparsePauliOp does not have .parameters because it is not allowed to be parametrized. # Can we handle this better than with an if? if not isinstance(hamiltonian, SparsePauliOp): - self._check_parameters(hamiltonian, hamiltonian_value_dict, t_param) - self.hamiltonian = hamiltonian - self.time = time - self.initial_state = initial_state - self.aux_operators = aux_operators - self.t_param = t_param - self.hamiltonian_value_dict = hamiltonian_value_dict + self._check_parameters(hamiltonian, self.hamiltonian_value_dict, self.t_param) + + self._hamiltonian = hamiltonian + + @property + def time(self) -> float: + """Returns time.""" + return self._time + + @time.setter + def time(self, time) -> None: + """ + Sets time and validates it. + + Raises: + ValueError: If time is not positive. + """ + self._time = None + if time <= 0: + raise ValueError( + f"Time of evolution provided is not positive, detected ``time={time}``." + ) + self._time = time + + @property + def initial_state(self) -> Union[StateFn, QuantumCircuit]: + """Returns an initial state.""" + return self._initial_state + + @initial_state.setter + def initial_state(self, initial_state) -> None: + """ + Sets an initial state and validates it. + + Raises: + ValueError: If no initial state is provided. + """ + self._initial_state = None + if initial_state is None: + raise ValueError( + "No ``initial_state`` provided for the EvolutionProblem. It is required." + ) + self._initial_state = initial_state def _check_parameters( self, @@ -101,7 +152,9 @@ def _check_parameters( t_param_set.add(t_param) hamiltonian_dict_param_set = set() if hamiltonian_value_dict is not None: - hamiltonian_dict_param_set.union(set(hamiltonian_value_dict.keys())) + hamiltonian_dict_param_set = hamiltonian_dict_param_set.union( + set(hamiltonian_value_dict.keys()) + ) params_set = t_param_set.union(hamiltonian_dict_param_set) hamiltonian_param_set = set(hamiltonian.parameters) if hamiltonian_param_set != params_set: diff --git a/test/python/algorithms/evolvers/test_evolution_problem.py b/test/python/algorithms/evolvers/test_evolution_problem.py index c9e6d4c0a53a..6458787fa29f 100644 --- a/test/python/algorithms/evolvers/test_evolution_problem.py +++ b/test/python/algorithms/evolvers/test_evolution_problem.py @@ -78,7 +78,7 @@ def test_init_all(self): @data([Y, -1, One], [Y, -1.2, One], [Y, 0, One], [None, 1, Zero], [Y, 2.5, None]) @unpack def test_init_errors(self, hamiltonian, time, initial_state): - """Tests that all default fields are initialized correctly.""" + """Tests expected errors are thrown on invalid input arguments.""" with assert_raises(ValueError): _ = EvolutionProblem(hamiltonian, time, initial_state) From 5a79df13eee09e85a6041004ac5a14dfe045ffa5 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 25 Mar 2022 15:59:27 +0100 Subject: [PATCH 112/145] Refactored test_trotter_qrte.py --- .../real/trotterization/test_trotter_qrte.py | 172 ++++++++---------- 1 file changed, 80 insertions(+), 92 deletions(-) diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index a508e0c21fff..d73b046a2f4a 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -14,8 +14,9 @@ import unittest +from qiskit.pulse import x from test.python.opflow import QiskitOpflowTestCase -from ddt import ddt, data +from ddt import ddt, data, unpack import numpy as np from numpy.testing import assert_raises from scipy.linalg import expm @@ -27,7 +28,7 @@ ) from qiskit.quantum_info import Statevector, SparsePauliOp, Pauli, PauliTable from qiskit.utils import algorithm_globals, QuantumInstance -from qiskit.circuit import Parameter +from qiskit.circuit import Parameter, Instruction from qiskit.opflow import ( X, Z, @@ -50,10 +51,10 @@ def setUp(self): super().setUp() self.seed = 50 algorithm_globals.random_seed = self.seed - backend = BasicAer.get_backend("statevector_simulator") + backend_statevector = BasicAer.get_backend("statevector_simulator") backend_qasm = BasicAer.get_backend("qasm_simulator") # TODO add to tests self.quantum_instance = QuantumInstance( - backend=backend, + backend=backend_statevector, shots=1, seed_simulator=self.seed, seed_transpiler=self.seed, @@ -66,44 +67,52 @@ def setUp(self): ) self.backends_dict = { "qi_sv": self.quantum_instance, - "qi_qasm": self.quantum_instance_qasm, - "b_sv": backend, + "b_sv": backend_statevector, "None": None, } - @data("qi_sv", "b_sv", "None") - def test_trotter_qrte_trotter_single_qubit(self, quantum_instanc): + self.backends_names = ["qi_sv", "b_sv", "None"] + self.backends_names_not_none = ["qi_sv", "b_sv"] + + @data( + (None, expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix())), + ( + SuzukiTrotter(), + expm(-1j * X.to_matrix() * 0.5) + @ expm(-1j * Z.to_matrix()) + @ expm(-1j * X.to_matrix() * 0.5), + ), + ) + @unpack + def test_trotter_qrte_trotter_single_qubit(self, product_formula, expected_state_part): """Test for default TrotterQRTE on a single qubit.""" operator = SummedOp([X, Z]) - # LieTrotter with 1 rep - quantum_instance = self.backends_dict[quantum_instanc] - - trotter_qrte = TrotterQRTE(quantum_instance=quantum_instance) initial_state = Zero - evolution_problem = EvolutionProblem(operator, 1, initial_state) - evolution_result = trotter_qrte.evolve(evolution_problem) + time = 1 + evolution_problem = EvolutionProblem(operator, time, initial_state) # Calculate the expected state - expected_state = ( - expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix()) @ initial_state.to_matrix() - ) + expected_state = expected_state_part @ initial_state.to_matrix() expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) + for backend_name in self.backends_names: + with self.subTest(msg=f"Test {backend_name} backend."): + backend = self.backends_dict[backend_name] + trotter_qrte = TrotterQRTE( + quantum_instance=backend, product_formula=product_formula + ) + evolution_result = trotter_qrte.evolve(evolution_problem) + np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - @data("qi_sv", "b_sv") - def test_trotter_qrte_trotter_single_qubit_aux_ops(self, quantum_instance): + def test_trotter_qrte_trotter_single_qubit_aux_ops(self): """Test for default TrotterQRTE on a single qubit with auxiliary operators.""" operator = SummedOp([X, Z]) # LieTrotter with 1 rep aux_ops = [X, Y] expectation = MatrixExpectation() - quantum_instance = self.backends_dict[quantum_instance] - trotter_qrte = TrotterQRTE(quantum_instance=quantum_instance, expectation=expectation) initial_state = Zero time = 3 evolution_problem = EvolutionProblem(operator, time, initial_state, aux_ops) - evolution_result = trotter_qrte.evolve(evolution_problem) # Calculate the expected state expected_state = ( expm(-time * 1j * Z.to_matrix()) @@ -113,10 +122,16 @@ def test_trotter_qrte_trotter_single_qubit_aux_ops(self, quantum_instance): expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) expected_aux_ops_evaluated = [(0.078073, 0.0), (0.268286, 0.0)] - np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - np.testing.assert_array_almost_equal( - evolution_result.aux_ops_evaluated, expected_aux_ops_evaluated - ) + for backend_name in self.backends_names_not_none: + with self.subTest(msg=f"Test {backend_name} backend."): + backend = self.backends_dict[backend_name] + trotter_qrte = TrotterQRTE(quantum_instance=backend, expectation=expectation) + evolution_result = trotter_qrte.evolve(evolution_problem) + + np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) + np.testing.assert_array_almost_equal( + evolution_result.aux_ops_evaluated, expected_aux_ops_evaluated + ) @data( SummedOp([(X ^ Y), (Y ^ X)]), @@ -128,7 +143,6 @@ def test_trotter_qrte_trotter_single_qubit_aux_ops(self, quantum_instance): def test_trotter_qrte_trotter_two_qubits(self, operator): """Test for TrotterQRTE on two qubits with various types of a Hamiltonian.""" # LieTrotter with 1 rep - trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = StateFn([1, 0, 0, 0]) # Calculate the expected state expected_state = initial_state.to_matrix() @@ -136,94 +150,68 @@ def test_trotter_qrte_trotter_two_qubits(self, operator): expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2, 2))) evolution_problem = EvolutionProblem(operator, 1, initial_state) - evolution_result = trotter_qrte.evolve(evolution_problem) - np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - - @data("qi_sv", "b_sv", "None") - def test_trotter_qrte_suzuki(self, quantum_instance): - """Test for TrotterQRTE with Suzuki.""" - operator = X + Z - # 2nd order Suzuki with 1 rep - quantum_instance = self.backends_dict[quantum_instance] - trotter_qrte = TrotterQRTE( - quantum_instance=quantum_instance, product_formula=SuzukiTrotter() - ) - initial_state = Zero - evolution_problem = EvolutionProblem(operator, 1, initial_state) - evolution_result = trotter_qrte.evolve(evolution_problem) - # Calculate the expected state - expected_state = ( - expm(-1j * X.to_matrix() * 0.5) - @ expm(-1j * Z.to_matrix()) - @ expm(-1j * X.to_matrix() * 0.5) - @ initial_state.to_matrix() - ) - expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) + for backend_name in self.backends_names: + with self.subTest(msg=f"Test {backend_name} backend."): + backend = self.backends_dict[backend_name] + trotter_qrte = TrotterQRTE(quantum_instance=backend) + evolution_result = trotter_qrte.evolve(evolution_problem) + np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - @data("qi_sv", "b_sv", "None") - def test_trotter_qrte_qdrift_fractional_time(self, quantum_instance): + @data(Zero, QuantumCircuit(1).compose(Pauli("Z").to_instruction(), [0])) + def test_trotter_qrte_qdrift_fractional_time(self, initial_state): """Test for TrotterQRTE with QDrift.""" - algorithm_globals.random_seed = 0 operator = SummedOp([X, Z]) - quantum_instance = self.backends_dict[quantum_instance] - # QDrift with one repetition - trotter_qrte = TrotterQRTE(quantum_instance=quantum_instance, product_formula=QDrift()) - initial_state = Zero - evolution_problem = EvolutionProblem(operator, 1, initial_state) - evolution_result = trotter_qrte.evolve(evolution_problem) + time = 1 + evolution_problem = EvolutionProblem(operator, time, initial_state) sampled_ops = [Z, X, X, X, Z, Z, Z, Z] evo_time = 0.25 # Calculate the expected state + if isinstance(initial_state, QuantumCircuit): + initial_state = StateFn(initial_state) expected_state = initial_state.to_matrix() for op in sampled_ops: expected_state = expm(-1j * op.to_matrix() * evo_time) @ expected_state expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - - @data("qi_sv", "b_sv", "None") - def test_trotter_qrte_qdrift_circuit(self, quantum_instance): - """Test for TrotterQRTE with QDrift.""" - algorithm_globals.random_seed = 0 - operator = SummedOp([X, Z]) - quantum_instance = self.backends_dict[quantum_instance] - # QDrift with one repetition - trotter_qrte = TrotterQRTE(quantum_instance=quantum_instance, product_formula=QDrift()) - initial_state = QuantumCircuit(1) - initial_state.append(X, [0]) - evolution_problem = EvolutionProblem(operator, 1, initial_state) - evolution_result = trotter_qrte.evolve(evolution_problem) - sampled_ops = [Z, X, X, X, Z, Z, Z, Z] - evo_time = 0.25 - # Calculate the expected state - expected_state = StateFn(initial_state).to_matrix() - for op in sampled_ops: - expected_state = expm(-1j * op.to_matrix() * evo_time) @ expected_state - expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - - np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) + for backend_name in self.backends_names: + with self.subTest(msg=f"Test {backend_name} backend."): + algorithm_globals.random_seed = 0 + backend = self.backends_dict[backend_name] + trotter_qrte = TrotterQRTE(quantum_instance=backend, product_formula=QDrift()) + evolution_result = trotter_qrte.evolve(evolution_problem) + np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) def test_trotter_qrte_trotter_binding_missing_dict(self): """Test for TrotterQRTE with binding and missing dictionary..""" t_param = Parameter("t") operator = X * t_param + Z - trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = Zero - with assert_raises(ValueError): - evolution_problem = EvolutionProblem(operator, 1, initial_state, t_param=t_param) - _ = trotter_qrte.evolve(evolution_problem) + time = 1 + evolution_problem = EvolutionProblem(operator, time, initial_state, t_param=t_param) + + for backend_name in self.backends_names: + with self.subTest(msg=f"Test {backend_name} backend."): + algorithm_globals.random_seed = 0 + backend = self.backends_dict[backend_name] + trotter_qrte = TrotterQRTE(quantum_instance=backend) + with assert_raises(ValueError): + _ = trotter_qrte.evolve(evolution_problem) def test_trotter_qrte_trotter_binding_missing_param(self): """Test for TrotterQRTE with binding and missing param.""" t_param = Parameter("t") operator = X * t_param + Z - trotter_qrte = TrotterQRTE(quantum_instance=self.quantum_instance) initial_state = Zero - with assert_raises(ValueError): - evolution_problem = EvolutionProblem(operator, 1, initial_state) - _ = trotter_qrte.evolve(evolution_problem) + + for backend_name in self.backends_names: + with self.subTest(msg=f"Test {backend_name} backend."): + algorithm_globals.random_seed = 0 + backend = self.backends_dict[backend_name] + trotter_qrte = TrotterQRTE(quantum_instance=backend) + with assert_raises(ValueError): + evolution_problem = EvolutionProblem(operator, 1, initial_state) + _ = trotter_qrte.evolve(evolution_problem) if __name__ == "__main__": From 3d45a955d104958b67df39291420d75fa406d021 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 25 Mar 2022 20:49:27 +0100 Subject: [PATCH 113/145] Removed global tolerance. --- .../algorithms/evolvers/evolution_problem.py | 4 ++ .../real/trotterization/trotter_qrte.py | 4 +- qiskit/utils/algorithm_globals.py | 11 ------ .../real/trotterization/test_trotter_qrte.py | 38 ++++++++----------- 4 files changed, 21 insertions(+), 36 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 1eac67d3f166..a1ea57c4e3b1 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -34,6 +34,7 @@ def __init__( time: float, initial_state: Union[StateFn, QuantumCircuit], aux_operators: Optional[ListOrDict[OperatorBase]] = None, + truncation_threshold: float = 1e-12, t_param: Optional[Parameter] = None, hamiltonian_value_dict: Optional[Dict[Parameter, Union[complex]]] = None, ): @@ -44,6 +45,8 @@ def __init__( initial_state: Quantum state to be evolved. aux_operators: Optional list of auxiliary operators to be evaluated with the evolved ``initial_state`` and their expectation values returned. + truncation_threshold: Defines a threshold under which values can be assumed to be 0. + Used when ``aux_operators`` is provided. t_param: Time parameter in case of a time-dependent Hamiltonian. This free parameter must be within the ``hamiltonian``. hamiltonian_value_dict: If the Hamiltonian contains free parameters, this @@ -61,6 +64,7 @@ def __init__( self.time = time self.initial_state = initial_state self.aux_operators = aux_operators + self.truncation_threshold = truncation_threshold @property def hamiltonian(self) -> OperatorBase: diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index dfa40917c70b..d53d46d3c326 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -31,7 +31,7 @@ from qiskit.providers import Backend, BaseBackend from qiskit.quantum_info import SparsePauliOp, Pauli from qiskit.synthesis import ProductFormula, LieTrotter -from qiskit.utils import QuantumInstance, algorithm_globals +from qiskit.utils import QuantumInstance class TrotterQRTE(RealEvolver): @@ -191,7 +191,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: quantum_state.primitive, evolution_problem.aux_operators, self._expectation, - algorithm_globals.numerical_tolerance_at_0, + evolution_problem.truncation_threshold, ) return EvolutionResult(evolved_state, evaluated_aux_ops) diff --git a/qiskit/utils/algorithm_globals.py b/qiskit/utils/algorithm_globals.py index dbbbaa34c82d..98f1b4f0fffa 100644 --- a/qiskit/utils/algorithm_globals.py +++ b/qiskit/utils/algorithm_globals.py @@ -35,7 +35,6 @@ def __init__(self) -> None: self._num_processes = QiskitAlgorithmGlobals.CPU_COUNT self._random = None self._massive = False - self._numerical_tolerance_at_0 = 1e-12 try: settings = get_config() self.num_processes = settings.get("num_processes", QiskitAlgorithmGlobals.CPU_COUNT) @@ -102,16 +101,6 @@ def massive(self, massive: bool) -> None: """Set massive to allow processing of large matrices or vectors.""" self._massive = massive - @property - def numerical_tolerance_at_0(self) -> float: - """Return numerical tolerance.""" - return self._numerical_tolerance_at_0 - - @numerical_tolerance_at_0.setter - def numerical_tolerance_at_0(self, numerical_tolerance_at_0: float) -> None: - """Set numerical tolerance.""" - self._numerical_tolerance_at_0 = numerical_tolerance_at_0 - # Global instance to be used as the entry point for globals. algorithm_globals = QiskitAlgorithmGlobals() diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index d73b046a2f4a..a9fbd753d6a3 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -14,7 +14,6 @@ import unittest -from qiskit.pulse import x from test.python.opflow import QiskitOpflowTestCase from ddt import ddt, data, unpack import numpy as np @@ -28,7 +27,7 @@ ) from qiskit.quantum_info import Statevector, SparsePauliOp, Pauli, PauliTable from qiskit.utils import algorithm_globals, QuantumInstance -from qiskit.circuit import Parameter, Instruction +from qiskit.circuit import Parameter from qiskit.opflow import ( X, Z, @@ -68,12 +67,14 @@ def setUp(self): self.backends_dict = { "qi_sv": self.quantum_instance, "b_sv": backend_statevector, + "b_qasm": backend_qasm, "None": None, } self.backends_names = ["qi_sv", "b_sv", "None"] self.backends_names_not_none = ["qi_sv", "b_sv"] + # TODO add valid param binding Hamiltonian @data( (None, expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix())), ( @@ -182,35 +183,26 @@ def test_trotter_qrte_qdrift_fractional_time(self, initial_state): evolution_result = trotter_qrte.evolve(evolution_problem) np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - def test_trotter_qrte_trotter_binding_missing_dict(self): - """Test for TrotterQRTE with binding and missing dictionary..""" - t_param = Parameter("t") - operator = X * t_param + Z + @data((Parameter("t"), {}), (None, {Parameter("t"): 2})) + @unpack + def test_trotter_qrte_trotter_errors(self, t_param, hamiltonian_value_dict): + """Test TrotterQRTE with raising errors.""" + operator = X * Parameter("t") + Z initial_state = Zero time = 1 - evolution_problem = EvolutionProblem(operator, time, initial_state, t_param=t_param) - - for backend_name in self.backends_names: - with self.subTest(msg=f"Test {backend_name} backend."): - algorithm_globals.random_seed = 0 - backend = self.backends_dict[backend_name] - trotter_qrte = TrotterQRTE(quantum_instance=backend) - with assert_raises(ValueError): - _ = trotter_qrte.evolve(evolution_problem) - - def test_trotter_qrte_trotter_binding_missing_param(self): - """Test for TrotterQRTE with binding and missing param.""" - t_param = Parameter("t") - operator = X * t_param + Z - initial_state = Zero - for backend_name in self.backends_names: with self.subTest(msg=f"Test {backend_name} backend."): algorithm_globals.random_seed = 0 backend = self.backends_dict[backend_name] trotter_qrte = TrotterQRTE(quantum_instance=backend) with assert_raises(ValueError): - evolution_problem = EvolutionProblem(operator, 1, initial_state) + evolution_problem = EvolutionProblem( + operator, + time, + initial_state, + t_param=t_param, + hamiltonian_value_dict=hamiltonian_value_dict, + ) _ = trotter_qrte.evolve(evolution_problem) From 373f2651c0e14e8d346d291baa2c4fd0aaa0c678 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 28 Mar 2022 10:28:34 +0200 Subject: [PATCH 114/145] CI fix. --- .../evolvers/real/trotterization/trotter_qrte.py | 2 +- .../algorithms/evolvers/test_evolution_problem.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index d53d46d3c326..4b5110e8ca1e 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -42,7 +42,7 @@ class TrotterQRTE(RealEvolver): .. jupyter-execute:: - from qiskit.opflow import X, Y, Zero + from qiskit.opflow import X, Z, Zero from qiskit.algorithms import EvolutionProblem, TrotterQRTE from qiskit import BasicAer from qiskit.utils import QuantumInstance diff --git a/test/python/algorithms/evolvers/test_evolution_problem.py b/test/python/algorithms/evolvers/test_evolution_problem.py index 6458787fa29f..5a6b93dcc6b3 100644 --- a/test/python/algorithms/evolvers/test_evolution_problem.py +++ b/test/python/algorithms/evolvers/test_evolution_problem.py @@ -12,11 +12,10 @@ """Test evolver problem class.""" import unittest - +from test.python.algorithms import QiskitAlgorithmsTestCase from ddt import data, ddt, unpack from numpy.testing import assert_raises -from test.python.algorithms import QiskitAlgorithmsTestCase from qiskit.algorithms.evolvers.evolution_problem import EvolutionProblem from qiskit.circuit import Parameter from qiskit.opflow import Y, Z, One, X, Zero @@ -58,7 +57,12 @@ def test_init_all(self): hamiltonian_value_dict = {t_parameter: 3.2} evo_problem = EvolutionProblem( - hamiltonian, time, initial_state, aux_operators, t_parameter, hamiltonian_value_dict + hamiltonian, + time, + initial_state, + aux_operators, + t_param=t_parameter, + hamiltonian_value_dict=hamiltonian_value_dict, ) expected_hamiltonian = Y + t_parameter * Z From bcfb4e24d75823ed332704353f3b3950039a3716 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 29 Mar 2022 12:31:46 +0200 Subject: [PATCH 115/145] Code review fixes. --- .../algorithms/evolvers/evolution_problem.py | 28 ++++++++----------- .../real/trotterization/trotter_qrte.py | 7 ++++- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index a1ea57c4e3b1..12c5f8fef979 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -18,7 +18,7 @@ from qiskit.circuit import Parameter from qiskit.opflow import OperatorBase, StateFn from ..list_or_dict import ListOrDict -from ...quantum_info import SparsePauliOp +from ...quantum_info.operators.base_operator import BaseOperator class EvolutionProblem: @@ -30,7 +30,7 @@ class EvolutionProblem: def __init__( self, - hamiltonian: OperatorBase, + hamiltonian: Union[OperatorBase, BaseOperator], time: float, initial_state: Union[StateFn, QuantumCircuit], aux_operators: Optional[ListOrDict[OperatorBase]] = None, @@ -67,27 +67,25 @@ def __init__( self.truncation_threshold = truncation_threshold @property - def hamiltonian(self) -> OperatorBase: + def hamiltonian(self) -> Union[OperatorBase, BaseOperator]: """Returns a hamiltonian.""" return self._hamiltonian @hamiltonian.setter - def hamiltonian(self, hamiltonian) -> None: + def hamiltonian(self, hamiltonian: Union[OperatorBase, BaseOperator]) -> None: """ Sets a hamiltonian and validates it. Raises: ValueError: If no Hamiltonian is provided. - ValueError: If Hamiltonian parameters cannot be bound with data provided. """ - self._hamiltonian = None if hamiltonian is None: raise ValueError( "No ``hamiltonian`` provided for the EvolutionProblem. It is required." ) - # TODO SparsePauliOp does not have .parameters because it is not allowed to be parametrized. - # Can we handle this better than with an if? - if not isinstance(hamiltonian, SparsePauliOp): + # TODO SparsePauliOp/BaseOperator does not have .parameters because it is not allowed to + # be parametrized. Can we handle this better than with an if? + if not isinstance(hamiltonian, BaseOperator): self._check_parameters(hamiltonian, self.hamiltonian_value_dict, self.t_param) self._hamiltonian = hamiltonian @@ -98,18 +96,15 @@ def time(self) -> float: return self._time @time.setter - def time(self, time) -> None: + def time(self, time: float) -> None: """ Sets time and validates it. Raises: ValueError: If time is not positive. """ - self._time = None if time <= 0: - raise ValueError( - f"Time of evolution provided is not positive, detected ``time={time}``." - ) + raise ValueError(f"Evolution time must be > 0 but was {time}.") self._time = time @property @@ -118,14 +113,13 @@ def initial_state(self) -> Union[StateFn, QuantumCircuit]: return self._initial_state @initial_state.setter - def initial_state(self, initial_state) -> None: + def initial_state(self, initial_state: Union[StateFn, QuantumCircuit]) -> None: """ Sets an initial state and validates it. Raises: ValueError: If no initial state is provided. """ - self._initial_state = None if initial_state is None: raise ValueError( "No ``initial_state`` provided for the EvolutionProblem. It is required." @@ -149,7 +143,7 @@ def _check_parameters( free parameter must be within the ``hamiltonian``. Raises: - ValueError: If there are unbound parameters in the Hamiltonian. + ValueError: If Hamiltonian parameters cannot be bound with data provided. """ t_param_set = set() if t_param is not None: diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 4b5110e8ca1e..2a70e20a5b15 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -35,7 +35,7 @@ class TrotterQRTE(RealEvolver): - """Class for performing Quantum Real Time Evolution using Trotterization. + """Quantum Real Time Evolution using Trotterization. Type of Trotterization is defined by a ProductFormula provided. Examples: @@ -162,6 +162,11 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: "``quantum_instance`` was provided." ) hamiltonian = evolution_problem.hamiltonian + if not isinstance(hamiltonian, (Pauli, PauliOp, SparsePauliOp, PauliSumOp, SummedOp)): + raise ValueError( + f"TrotterQRTE only accepts Pauli | PauliOp | SparsePauliOp | " + f"PauliSumOp | SummedOp, {type(hamiltonian)} provided." + ) if not isinstance(hamiltonian, SparsePauliOp): # TODO can we handle it better? hamiltonian = hamiltonian.bind_parameters(evolution_problem.hamiltonian_value_dict) if isinstance(hamiltonian, SummedOp): From f29fb943ad68fce88bb2b2a58996de4999f7f787 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 29 Mar 2022 21:00:09 +0200 Subject: [PATCH 116/145] Code refactoring. --- .../algorithms/evolvers/evolution_problem.py | 38 +++++++++---------- .../real/trotterization/trotter_qrte.py | 13 ++++++- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 12c5f8fef979..535a81c4b8d8 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -83,11 +83,8 @@ def hamiltonian(self, hamiltonian: Union[OperatorBase, BaseOperator]) -> None: raise ValueError( "No ``hamiltonian`` provided for the EvolutionProblem. It is required." ) - # TODO SparsePauliOp/BaseOperator does not have .parameters because it is not allowed to - # be parametrized. Can we handle this better than with an if? - if not isinstance(hamiltonian, BaseOperator): - self._check_parameters(hamiltonian, self.hamiltonian_value_dict, self.t_param) + self._check_parameters(hamiltonian, self.hamiltonian_value_dict, self.t_param) self._hamiltonian = hamiltonian @property @@ -128,7 +125,7 @@ def initial_state(self, initial_state: Union[StateFn, QuantumCircuit]) -> None: def _check_parameters( self, - hamiltonian: OperatorBase, + hamiltonian: Union[OperatorBase, BaseOperator], hamiltonian_value_dict: Optional[Dict[Parameter, Union[complex]]] = None, t_param: Optional[Parameter] = None, ) -> None: @@ -145,18 +142,19 @@ def _check_parameters( Raises: ValueError: If Hamiltonian parameters cannot be bound with data provided. """ - t_param_set = set() - if t_param is not None: - t_param_set.add(t_param) - hamiltonian_dict_param_set = set() - if hamiltonian_value_dict is not None: - hamiltonian_dict_param_set = hamiltonian_dict_param_set.union( - set(hamiltonian_value_dict.keys()) - ) - params_set = t_param_set.union(hamiltonian_dict_param_set) - hamiltonian_param_set = set(hamiltonian.parameters) - if hamiltonian_param_set != params_set: - raise ValueError( - f"Provided parameters {params_set} do not match Hamiltonian parameters " - f"{hamiltonian_param_set}." - ) + if isinstance(hamiltonian, OperatorBase): + t_param_set = set() + if t_param is not None: + t_param_set.add(t_param) + hamiltonian_dict_param_set = set() + if hamiltonian_value_dict is not None: + hamiltonian_dict_param_set = hamiltonian_dict_param_set.union( + set(hamiltonian_value_dict.keys()) + ) + params_set = t_param_set.union(hamiltonian_dict_param_set) + hamiltonian_param_set = set(hamiltonian.parameters) + if hamiltonian_param_set != params_set: + raise ValueError( + f"Provided parameters {params_set} do not match Hamiltonian parameters " + f"{hamiltonian_param_set}." + ) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 2a70e20a5b15..06fcae49dbaf 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -26,6 +26,9 @@ CircuitSampler, PauliSumOp, StateFn, + ListOp, + CircuitStateFn, + OperatorBase, ) from qiskit.circuit.library import PauliEvolutionGate from qiskit.providers import Backend, BaseBackend @@ -71,9 +74,15 @@ def __init__( expectation: An instance of ExpectationBase which defines a method for calculating expectation values of EvolutionProblem.aux_operators. quantum_instance: A quantum instance used for calculations. If not provided, - calculations are performed clasically which work reasonably only for small systems. + calculations are performed classically which work reasonably only for small systems. In case of auxiliary operators provided in ``EvolutionProblem``, a quantum instance is required. + + .. note:: + + Shot-based simulators like, e.g., the ``qasm_simulator`` will return counts sampled + from an evolved state, not the description of the state itself as it happens for the + ``statevector_simulator`` for example. """ if product_formula is None: product_formula = LieTrotter() @@ -167,7 +176,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: f"TrotterQRTE only accepts Pauli | PauliOp | SparsePauliOp | " f"PauliSumOp | SummedOp, {type(hamiltonian)} provided." ) - if not isinstance(hamiltonian, SparsePauliOp): # TODO can we handle it better? + if isinstance(hamiltonian, OperatorBase): hamiltonian = hamiltonian.bind_parameters(evolution_problem.hamiltonian_value_dict) if isinstance(hamiltonian, SummedOp): hamiltonian = self._summed_op_to_pauli_sum_op(hamiltonian) From 2c37cc1f9f7ed0253ba40c4017cb24ecf577a709 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 29 Mar 2022 21:03:57 +0200 Subject: [PATCH 117/145] Prepared qasm unit test. --- .../real/trotterization/test_trotter_qrte.py | 235 ++++++++++-------- 1 file changed, 129 insertions(+), 106 deletions(-) diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index a9fbd753d6a3..2f1c473cca27 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -13,8 +13,9 @@ """ Test TrotterQRTE. """ import unittest - from test.python.opflow import QiskitOpflowTestCase +from math import sqrt +from typing import List from ddt import ddt, data, unpack import numpy as np from numpy.testing import assert_raises @@ -66,14 +67,26 @@ def setUp(self): ) self.backends_dict = { "qi_sv": self.quantum_instance, + "qi_qasm": self.quantum_instance_qasm, "b_sv": backend_statevector, "b_qasm": backend_qasm, "None": None, } - self.backends_names = ["qi_sv", "b_sv", "None"] + self.backends_names = ["qi_qasm", "b_sv", "None", "b_qasm", "qi_sv"] self.backends_names_not_none = ["qi_sv", "b_sv"] + def calculate_counts(self, statevector: VectorStateFn) -> List[float]: + + primitive = statevector.primitive + coeff1 = primitive.data[0] + coeff2 = primitive.data[1] + + sampled1 = sqrt(coeff1 * np.conjugate(coeff1)) + sampled2 = sqrt(coeff2 * np.conjugate(coeff2)) + + return [sampled1, sampled2] + # TODO add valid param binding Hamiltonian @data( (None, expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix())), @@ -88,122 +101,132 @@ def setUp(self): def test_trotter_qrte_trotter_single_qubit(self, product_formula, expected_state_part): """Test for default TrotterQRTE on a single qubit.""" operator = SummedOp([X, Z]) - initial_state = Zero + initial_state = StateFn([1, 0]) time = 1 evolution_problem = EvolutionProblem(operator, time, initial_state) # Calculate the expected state expected_state = expected_state_part @ initial_state.to_matrix() - expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) for backend_name in self.backends_names: with self.subTest(msg=f"Test {backend_name} backend."): backend = self.backends_dict[backend_name] + expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) + trotter_qrte = TrotterQRTE( quantum_instance=backend, product_formula=product_formula ) - evolution_result = trotter_qrte.evolve(evolution_problem) - np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - - def test_trotter_qrte_trotter_single_qubit_aux_ops(self): - """Test for default TrotterQRTE on a single qubit with auxiliary operators.""" - operator = SummedOp([X, Z]) - # LieTrotter with 1 rep - aux_ops = [X, Y] - expectation = MatrixExpectation() - - initial_state = Zero - time = 3 - evolution_problem = EvolutionProblem(operator, time, initial_state, aux_ops) - # Calculate the expected state - expected_state = ( - expm(-time * 1j * Z.to_matrix()) - @ expm(-time * 1j * X.to_matrix()) - @ initial_state.to_matrix() - ) - expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - expected_aux_ops_evaluated = [(0.078073, 0.0), (0.268286, 0.0)] - - for backend_name in self.backends_names_not_none: - with self.subTest(msg=f"Test {backend_name} backend."): - backend = self.backends_dict[backend_name] - trotter_qrte = TrotterQRTE(quantum_instance=backend, expectation=expectation) - evolution_result = trotter_qrte.evolve(evolution_problem) - - np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - np.testing.assert_array_almost_equal( - evolution_result.aux_ops_evaluated, expected_aux_ops_evaluated - ) - - @data( - SummedOp([(X ^ Y), (Y ^ X)]), - (Z ^ Z) + (Z ^ I) + (I ^ Z), - Y ^ Y, - SparsePauliOp(Pauli("XI")), - SparsePauliOp(PauliTable.from_labels(["XX", "ZZ"])), - ) - def test_trotter_qrte_trotter_two_qubits(self, operator): - """Test for TrotterQRTE on two qubits with various types of a Hamiltonian.""" - # LieTrotter with 1 rep - initial_state = StateFn([1, 0, 0, 0]) - # Calculate the expected state - expected_state = initial_state.to_matrix() - expected_state = expm(-1j * operator.to_matrix()) @ expected_state - expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2, 2))) - - evolution_problem = EvolutionProblem(operator, 1, initial_state) - - for backend_name in self.backends_names: - with self.subTest(msg=f"Test {backend_name} backend."): - backend = self.backends_dict[backend_name] - trotter_qrte = TrotterQRTE(quantum_instance=backend) - evolution_result = trotter_qrte.evolve(evolution_problem) - np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - - @data(Zero, QuantumCircuit(1).compose(Pauli("Z").to_instruction(), [0])) - def test_trotter_qrte_qdrift_fractional_time(self, initial_state): - """Test for TrotterQRTE with QDrift.""" - operator = SummedOp([X, Z]) - time = 1 - evolution_problem = EvolutionProblem(operator, time, initial_state) - sampled_ops = [Z, X, X, X, Z, Z, Z, Z] - evo_time = 0.25 - # Calculate the expected state - if isinstance(initial_state, QuantumCircuit): - initial_state = StateFn(initial_state) - expected_state = initial_state.to_matrix() - for op in sampled_ops: - expected_state = expm(-1j * op.to_matrix() * evo_time) @ expected_state - expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - - for backend_name in self.backends_names: - with self.subTest(msg=f"Test {backend_name} backend."): - algorithm_globals.random_seed = 0 - backend = self.backends_dict[backend_name] - trotter_qrte = TrotterQRTE(quantum_instance=backend, product_formula=QDrift()) - evolution_result = trotter_qrte.evolve(evolution_problem) - np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - - @data((Parameter("t"), {}), (None, {Parameter("t"): 2})) - @unpack - def test_trotter_qrte_trotter_errors(self, t_param, hamiltonian_value_dict): - """Test TrotterQRTE with raising errors.""" - operator = X * Parameter("t") + Z - initial_state = Zero - time = 1 - for backend_name in self.backends_names: - with self.subTest(msg=f"Test {backend_name} backend."): - algorithm_globals.random_seed = 0 - backend = self.backends_dict[backend_name] - trotter_qrte = TrotterQRTE(quantum_instance=backend) - with assert_raises(ValueError): - evolution_problem = EvolutionProblem( - operator, - time, - initial_state, - t_param=t_param, - hamiltonian_value_dict=hamiltonian_value_dict, + evolution_result_state = trotter_qrte.evolve(evolution_problem).evolved_state + + if backend_name in {"qi_qasm", "b_qasm"}: + evolution_result_state = evolution_result_state.to_matrix()[0] + decimal = 1 + expected_evolved_state = self.calculate_counts(expected_evolved_state) + np.testing.assert_almost_equal( + evolution_result_state, expected_evolved_state, decimal=decimal ) - _ = trotter_qrte.evolve(evolution_problem) + else: + np.testing.assert_equal(evolution_result_state, expected_evolved_state) + + # def test_trotter_qrte_trotter_single_qubit_aux_ops(self): + # """Test for default TrotterQRTE on a single qubit with auxiliary operators.""" + # operator = SummedOp([X, Z]) + # # LieTrotter with 1 rep + # aux_ops = [X, Y] + # expectation = MatrixExpectation() + # + # initial_state = Zero + # time = 3 + # evolution_problem = EvolutionProblem(operator, time, initial_state, aux_ops) + # # Calculate the expected state + # expected_state = ( + # expm(-time * 1j * Z.to_matrix()) + # @ expm(-time * 1j * X.to_matrix()) + # @ initial_state.to_matrix() + # ) + # expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) + # expected_aux_ops_evaluated = [(0.078073, 0.0), (0.268286, 0.0)] + # + # for backend_name in self.backends_names_not_none: + # with self.subTest(msg=f"Test {backend_name} backend."): + # backend = self.backends_dict[backend_name] + # trotter_qrte = TrotterQRTE(quantum_instance=backend, expectation=expectation) + # evolution_result = trotter_qrte.evolve(evolution_problem) + # + # np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) + # np.testing.assert_array_almost_equal( + # evolution_result.aux_ops_evaluated, expected_aux_ops_evaluated + # ) + # + # @data( + # SummedOp([(X ^ Y), (Y ^ X)]), + # (Z ^ Z) + (Z ^ I) + (I ^ Z), + # Y ^ Y, + # SparsePauliOp(Pauli("XI")), + # SparsePauliOp(PauliTable.from_labels(["XX", "ZZ"])), + # ) + # def test_trotter_qrte_trotter_two_qubits(self, operator): + # """Test for TrotterQRTE on two qubits with various types of a Hamiltonian.""" + # # LieTrotter with 1 rep + # initial_state = StateFn([1, 0, 0, 0]) + # # Calculate the expected state + # expected_state = initial_state.to_matrix() + # expected_state = expm(-1j * operator.to_matrix()) @ expected_state + # expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2, 2))) + # + # evolution_problem = EvolutionProblem(operator, 1, initial_state) + # + # for backend_name in self.backends_names: + # with self.subTest(msg=f"Test {backend_name} backend."): + # backend = self.backends_dict[backend_name] + # trotter_qrte = TrotterQRTE(quantum_instance=backend) + # evolution_result = trotter_qrte.evolve(evolution_problem) + # np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) + # + # @data(Zero, QuantumCircuit(1).compose(Pauli("Z").to_instruction(), [0])) + # def test_trotter_qrte_qdrift_fractional_time(self, initial_state): + # """Test for TrotterQRTE with QDrift.""" + # operator = SummedOp([X, Z]) + # time = 1 + # evolution_problem = EvolutionProblem(operator, time, initial_state) + # sampled_ops = [Z, X, X, X, Z, Z, Z, Z] + # evo_time = 0.25 + # # Calculate the expected state + # if isinstance(initial_state, QuantumCircuit): + # initial_state = StateFn(initial_state) + # expected_state = initial_state.to_matrix() + # for op in sampled_ops: + # expected_state = expm(-1j * op.to_matrix() * evo_time) @ expected_state + # expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) + # + # for backend_name in self.backends_names: + # with self.subTest(msg=f"Test {backend_name} backend."): + # algorithm_globals.random_seed = 0 + # backend = self.backends_dict[backend_name] + # trotter_qrte = TrotterQRTE(quantum_instance=backend, product_formula=QDrift()) + # evolution_result = trotter_qrte.evolve(evolution_problem) + # np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) + # + # @data((Parameter("t"), {}), (None, {Parameter("t"): 2})) + # @unpack + # def test_trotter_qrte_trotter_errors(self, t_param, hamiltonian_value_dict): + # """Test TrotterQRTE with raising errors.""" + # operator = X * Parameter("t") + Z + # initial_state = Zero + # time = 1 + # for backend_name in self.backends_names: + # with self.subTest(msg=f"Test {backend_name} backend."): + # algorithm_globals.random_seed = 0 + # backend = self.backends_dict[backend_name] + # trotter_qrte = TrotterQRTE(quantum_instance=backend) + # with assert_raises(ValueError): + # evolution_problem = EvolutionProblem( + # operator, + # time, + # initial_state, + # t_param=t_param, + # hamiltonian_value_dict=hamiltonian_value_dict, + # ) + # _ = trotter_qrte.evolve(evolution_problem) if __name__ == "__main__": From dd987cea22e2ebf01c32c3f9ed68f7ecaae84b62 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 30 Mar 2022 20:54:35 +0200 Subject: [PATCH 118/145] Removing state eval. --- .../real/trotterization/test_trotter_qrte.py | 282 +++++++++--------- 1 file changed, 147 insertions(+), 135 deletions(-) diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index 2f1c473cca27..66d6d8665e3c 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -52,7 +52,7 @@ def setUp(self): self.seed = 50 algorithm_globals.random_seed = self.seed backend_statevector = BasicAer.get_backend("statevector_simulator") - backend_qasm = BasicAer.get_backend("qasm_simulator") # TODO add to tests + backend_qasm = BasicAer.get_backend("qasm_simulator") self.quantum_instance = QuantumInstance( backend=backend_statevector, shots=1, @@ -74,159 +74,171 @@ def setUp(self): } self.backends_names = ["qi_qasm", "b_sv", "None", "b_qasm", "qi_sv"] - self.backends_names_not_none = ["qi_sv", "b_sv"] - - def calculate_counts(self, statevector: VectorStateFn) -> List[float]: - - primitive = statevector.primitive - coeff1 = primitive.data[0] - coeff2 = primitive.data[1] - - sampled1 = sqrt(coeff1 * np.conjugate(coeff1)) - sampled2 = sqrt(coeff2 * np.conjugate(coeff2)) - - return [sampled1, sampled2] + self.backends_names_not_none = ["qi_sv", "b_sv", "qi_qasm"] # TODO add valid param binding Hamiltonian @data( - (None, expm(-1j * Z.to_matrix()) @ expm(-1j * X.to_matrix())), + ( + None, + VectorStateFn( + Statevector([0.29192658 - 0.45464871j, 0.70807342 - 0.45464871j], dims=(2,)) + ), + ), ( SuzukiTrotter(), - expm(-1j * X.to_matrix() * 0.5) - @ expm(-1j * Z.to_matrix()) - @ expm(-1j * X.to_matrix() * 0.5), + VectorStateFn(Statevector([0.29192658 - 0.84147098j, 0.0 - 0.45464871j], dims=(2,))), ), ) @unpack - def test_trotter_qrte_trotter_single_qubit(self, product_formula, expected_state_part): + def test_trotter_qrte_trotter_single_qubit(self, product_formula, expected_state): """Test for default TrotterQRTE on a single qubit.""" operator = SummedOp([X, Z]) initial_state = StateFn([1, 0]) time = 1 evolution_problem = EvolutionProblem(operator, time, initial_state) - # Calculate the expected state - expected_state = expected_state_part @ initial_state.to_matrix() - for backend_name in self.backends_names: + trotter_qrte = TrotterQRTE(product_formula=product_formula) + evolution_result_state_circuit = trotter_qrte.evolve(evolution_problem).evolved_state + + np.testing.assert_equal(evolution_result_state_circuit.eval(), expected_state) + + def test_trotter_qrte_trotter_single_qubit_aux_ops(self): + """Test for default TrotterQRTE on a single qubit with auxiliary operators.""" + operator = SummedOp([X, Z]) + # LieTrotter with 1 rep + aux_ops = [X, Y] + expectation = MatrixExpectation() + + initial_state = Zero + time = 3 + evolution_problem = EvolutionProblem(operator, time, initial_state, aux_ops) + + expected_evolved_state = VectorStateFn( + Statevector([0.98008514 + 0.13970775j, 0.01991486 + 0.13970775j], dims=(2,)) + ) + expected_aux_ops_evaluated = [(0.078073, 0.0), (0.268286, 0.0)] + + for backend_name in self.backends_names_not_none: with self.subTest(msg=f"Test {backend_name} backend."): + algorithm_globals.random_seed = 0 backend = self.backends_dict[backend_name] - expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) + trotter_qrte = TrotterQRTE(quantum_instance=backend, expectation=expectation) + evolution_result = trotter_qrte.evolve(evolution_problem) + print(evolution_result.aux_ops_evaluated) + np.testing.assert_equal( + evolution_result.evolved_state.eval(), expected_evolved_state + ) + np.testing.assert_array_almost_equal( + evolution_result.aux_ops_evaluated, expected_aux_ops_evaluated + ) - trotter_qrte = TrotterQRTE( - quantum_instance=backend, product_formula=product_formula + @data( + ( + SummedOp([(X ^ Y), (Y ^ X)]), + VectorStateFn( + Statevector( + [-0.41614684 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.90929743 + 0.0j], dims=(2, 2) + ) + ), + ), + ( + (Z ^ Z) + (Z ^ I) + (I ^ Z), + VectorStateFn( + Statevector( + [-0.9899925 - 0.14112001j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], dims=(2, 2) + ) + ), + ), + ( + Y ^ Y, + VectorStateFn( + Statevector( + [0.54030231 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.84147098j], dims=(2, 2) + ) + ), + ), + ( + SparsePauliOp(Pauli("XI")), + VectorStateFn( + Statevector( + [0.54030231 + 0.0j, 0.0 + 0.0j, 0.0 - 0.84147098j, 0.0 + 0.0j], dims=(2, 2) + ) + ), + ), + ( + SparsePauliOp(PauliTable.from_labels(["XX", "ZZ"])), + VectorStateFn( + Statevector( + [0.29192658 - 0.45464871j, 0.0 + 0.0j, 0.0 + 0.0j, -0.70807342 - 0.45464871j], + dims=(2, 2), ) - evolution_result_state = trotter_qrte.evolve(evolution_problem).evolved_state - - if backend_name in {"qi_qasm", "b_qasm"}: - evolution_result_state = evolution_result_state.to_matrix()[0] - decimal = 1 - expected_evolved_state = self.calculate_counts(expected_evolved_state) - np.testing.assert_almost_equal( - evolution_result_state, expected_evolved_state, decimal=decimal + ), + ), + ) + @unpack + def test_trotter_qrte_trotter_two_qubits(self, operator, expected_state): + """Test for TrotterQRTE on two qubits with various types of a Hamiltonian.""" + # LieTrotter with 1 rep + initial_state = StateFn([1, 0, 0, 0]) + + evolution_problem = EvolutionProblem(operator, 1, initial_state) + for backend_name in self.backends_names: + with self.subTest(msg=f"Test {backend_name} backend."): + backend = self.backends_dict[backend_name] + trotter_qrte = TrotterQRTE(quantum_instance=backend) + evolution_result = trotter_qrte.evolve(evolution_problem) + np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) + + @data( + ( + Zero, + VectorStateFn( + Statevector([0.23071786 - 0.69436148j, 0.4646314 - 0.49874749j], dims=(2,)) + ), + ), + ( + QuantumCircuit(1).compose(Pauli("Z").to_instruction(), [0]), + VectorStateFn( + Statevector([0.23071786 - 0.69436148j, 0.4646314 - 0.49874749j], dims=(2,)) + ), + ), + ) + @unpack + def test_trotter_qrte_qdrift_fractional_time(self, initial_state, expected_state): + """Test for TrotterQRTE with QDrift.""" + operator = SummedOp([X, Z]) + time = 1 + evolution_problem = EvolutionProblem(operator, time, initial_state) + + for backend_name in self.backends_names: + with self.subTest(msg=f"Test {backend_name} backend."): + algorithm_globals.random_seed = 0 + backend = self.backends_dict[backend_name] + trotter_qrte = TrotterQRTE(quantum_instance=backend, product_formula=QDrift()) + evolution_result = trotter_qrte.evolve(evolution_problem) + np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) + + @data((Parameter("t"), {}), (None, {Parameter("t"): 2})) + @unpack + def test_trotter_qrte_trotter_errors(self, t_param, hamiltonian_value_dict): + """Test TrotterQRTE with raising errors.""" + operator = X * Parameter("t") + Z + initial_state = Zero + time = 1 + for backend_name in self.backends_names: + with self.subTest(msg=f"Test {backend_name} backend."): + algorithm_globals.random_seed = 0 + backend = self.backends_dict[backend_name] + trotter_qrte = TrotterQRTE(quantum_instance=backend) + with assert_raises(ValueError): + evolution_problem = EvolutionProblem( + operator, + time, + initial_state, + t_param=t_param, + hamiltonian_value_dict=hamiltonian_value_dict, ) - else: - np.testing.assert_equal(evolution_result_state, expected_evolved_state) - - # def test_trotter_qrte_trotter_single_qubit_aux_ops(self): - # """Test for default TrotterQRTE on a single qubit with auxiliary operators.""" - # operator = SummedOp([X, Z]) - # # LieTrotter with 1 rep - # aux_ops = [X, Y] - # expectation = MatrixExpectation() - # - # initial_state = Zero - # time = 3 - # evolution_problem = EvolutionProblem(operator, time, initial_state, aux_ops) - # # Calculate the expected state - # expected_state = ( - # expm(-time * 1j * Z.to_matrix()) - # @ expm(-time * 1j * X.to_matrix()) - # @ initial_state.to_matrix() - # ) - # expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - # expected_aux_ops_evaluated = [(0.078073, 0.0), (0.268286, 0.0)] - # - # for backend_name in self.backends_names_not_none: - # with self.subTest(msg=f"Test {backend_name} backend."): - # backend = self.backends_dict[backend_name] - # trotter_qrte = TrotterQRTE(quantum_instance=backend, expectation=expectation) - # evolution_result = trotter_qrte.evolve(evolution_problem) - # - # np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - # np.testing.assert_array_almost_equal( - # evolution_result.aux_ops_evaluated, expected_aux_ops_evaluated - # ) - # - # @data( - # SummedOp([(X ^ Y), (Y ^ X)]), - # (Z ^ Z) + (Z ^ I) + (I ^ Z), - # Y ^ Y, - # SparsePauliOp(Pauli("XI")), - # SparsePauliOp(PauliTable.from_labels(["XX", "ZZ"])), - # ) - # def test_trotter_qrte_trotter_two_qubits(self, operator): - # """Test for TrotterQRTE on two qubits with various types of a Hamiltonian.""" - # # LieTrotter with 1 rep - # initial_state = StateFn([1, 0, 0, 0]) - # # Calculate the expected state - # expected_state = initial_state.to_matrix() - # expected_state = expm(-1j * operator.to_matrix()) @ expected_state - # expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2, 2))) - # - # evolution_problem = EvolutionProblem(operator, 1, initial_state) - # - # for backend_name in self.backends_names: - # with self.subTest(msg=f"Test {backend_name} backend."): - # backend = self.backends_dict[backend_name] - # trotter_qrte = TrotterQRTE(quantum_instance=backend) - # evolution_result = trotter_qrte.evolve(evolution_problem) - # np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - # - # @data(Zero, QuantumCircuit(1).compose(Pauli("Z").to_instruction(), [0])) - # def test_trotter_qrte_qdrift_fractional_time(self, initial_state): - # """Test for TrotterQRTE with QDrift.""" - # operator = SummedOp([X, Z]) - # time = 1 - # evolution_problem = EvolutionProblem(operator, time, initial_state) - # sampled_ops = [Z, X, X, X, Z, Z, Z, Z] - # evo_time = 0.25 - # # Calculate the expected state - # if isinstance(initial_state, QuantumCircuit): - # initial_state = StateFn(initial_state) - # expected_state = initial_state.to_matrix() - # for op in sampled_ops: - # expected_state = expm(-1j * op.to_matrix() * evo_time) @ expected_state - # expected_evolved_state = VectorStateFn(Statevector(expected_state, dims=(2,))) - # - # for backend_name in self.backends_names: - # with self.subTest(msg=f"Test {backend_name} backend."): - # algorithm_globals.random_seed = 0 - # backend = self.backends_dict[backend_name] - # trotter_qrte = TrotterQRTE(quantum_instance=backend, product_formula=QDrift()) - # evolution_result = trotter_qrte.evolve(evolution_problem) - # np.testing.assert_equal(evolution_result.evolved_state, expected_evolved_state) - # - # @data((Parameter("t"), {}), (None, {Parameter("t"): 2})) - # @unpack - # def test_trotter_qrte_trotter_errors(self, t_param, hamiltonian_value_dict): - # """Test TrotterQRTE with raising errors.""" - # operator = X * Parameter("t") + Z - # initial_state = Zero - # time = 1 - # for backend_name in self.backends_names: - # with self.subTest(msg=f"Test {backend_name} backend."): - # algorithm_globals.random_seed = 0 - # backend = self.backends_dict[backend_name] - # trotter_qrte = TrotterQRTE(quantum_instance=backend) - # with assert_raises(ValueError): - # evolution_problem = EvolutionProblem( - # operator, - # time, - # initial_state, - # t_param=t_param, - # hamiltonian_value_dict=hamiltonian_value_dict, - # ) - # _ = trotter_qrte.evolve(evolution_problem) + _ = trotter_qrte.evolve(evolution_problem) if __name__ == "__main__": From 3726b8b679b9d0a435e281e2dcf68f181e1cccdf Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Wed, 30 Mar 2022 20:54:50 +0200 Subject: [PATCH 119/145] Refactored and updated tests. --- .../real/trotterization/trotter_qrte.py | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index 06fcae49dbaf..c6d891418605 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -73,16 +73,8 @@ def __init__( first order product formula with a single repetition. expectation: An instance of ExpectationBase which defines a method for calculating expectation values of EvolutionProblem.aux_operators. - quantum_instance: A quantum instance used for calculations. If not provided, - calculations are performed classically which work reasonably only for small systems. - In case of auxiliary operators provided in ``EvolutionProblem``, a quantum instance - is required. - - .. note:: - - Shot-based simulators like, e.g., the ``qasm_simulator`` will return counts sampled - from an evolved state, not the description of the state itself as it happens for the - ``statevector_simulator`` for example. + quantum_instance: A quantum instance used for calculating expectation values of + EvolutionProblem.aux_operators. """ if product_formula is None: product_formula = LieTrotter() @@ -139,7 +131,9 @@ def supports_aux_operators(cls) -> bool: def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: """ Evolves a quantum state for a given time using the Trotterization method - based on a product formula provided. + based on a product formula provided. The result is provided in the form of a quantum + circuit. If auxiliary operators are included in the ``evolution_problem``, they are + evaluated on an evolved state using a backend provided. .. note:: Time-dependent Hamiltonians are not yet supported. @@ -150,7 +144,8 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: are supported by TrotterQRTE. Returns: - Evolution result that includes an evolved state. + Evolution result that includes an evolved state as a quantum circuit and, optionally, + auxiliary operators evaluated for a resulting state on a backend. Raises: ValueError: If ``t_param`` is not set to None in the EvolutionProblem (feature not @@ -189,11 +184,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: initial_state = evolution_problem.initial_state if isinstance(initial_state, QuantumCircuit): initial_state = StateFn(initial_state) - quantum_state = evolution_gate @ initial_state - if self._circuit_sampler is not None: - quantum_state = self._circuit_sampler.convert(quantum_state) - - evolved_state = quantum_state.eval() + evolved_state = evolution_gate @ initial_state else: raise ValueError("``initial_state`` must be provided in the EvolutionProblem.") @@ -202,7 +193,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: if evolution_problem.aux_operators is not None: evaluated_aux_ops = eval_observables( self._quantum_instance, - quantum_state.primitive, + evolved_state.primitive, evolution_problem.aux_operators, self._expectation, evolution_problem.truncation_threshold, From f8a149aedbccb448bc364f47683dca4e3b749268 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 31 Mar 2022 18:45:48 +0200 Subject: [PATCH 120/145] Added qasm test for aux ops. --- .../real/trotterization/test_trotter_qrte.py | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index 66d6d8665e3c..84b678994acd 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -13,13 +13,11 @@ """ Test TrotterQRTE. """ import unittest + from test.python.opflow import QiskitOpflowTestCase -from math import sqrt -from typing import List from ddt import ddt, data, unpack import numpy as np from numpy.testing import assert_raises -from scipy.linalg import expm from qiskit import BasicAer, QuantumCircuit from qiskit.algorithms import EvolutionProblem @@ -37,8 +35,8 @@ StateFn, I, Y, - MatrixExpectation, SummedOp, + ExpectationFactory, ) from qiskit.synthesis import SuzukiTrotter, QDrift @@ -61,7 +59,7 @@ def setUp(self): ) self.quantum_instance_qasm = QuantumInstance( backend=backend_qasm, - shots=4000, + shots=8000, seed_simulator=self.seed, seed_transpiler=self.seed, ) @@ -69,11 +67,10 @@ def setUp(self): "qi_sv": self.quantum_instance, "qi_qasm": self.quantum_instance_qasm, "b_sv": backend_statevector, - "b_qasm": backend_qasm, "None": None, } - self.backends_names = ["qi_qasm", "b_sv", "None", "b_qasm", "qi_sv"] + self.backends_names = ["qi_qasm", "b_sv", "None", "qi_sv"] self.backends_names_not_none = ["qi_sv", "b_sv", "qi_qasm"] # TODO add valid param binding Hamiltonian @@ -107,7 +104,6 @@ def test_trotter_qrte_trotter_single_qubit_aux_ops(self): operator = SummedOp([X, Z]) # LieTrotter with 1 rep aux_ops = [X, Y] - expectation = MatrixExpectation() initial_state = Zero time = 3 @@ -117,17 +113,27 @@ def test_trotter_qrte_trotter_single_qubit_aux_ops(self): Statevector([0.98008514 + 0.13970775j, 0.01991486 + 0.13970775j], dims=(2,)) ) expected_aux_ops_evaluated = [(0.078073, 0.0), (0.268286, 0.0)] + expected_aux_ops_evaluated_qasm = [ + (0.05799999999999995, 0.011161518713866855), + (0.2495, 0.010826759383582883), + ] for backend_name in self.backends_names_not_none: with self.subTest(msg=f"Test {backend_name} backend."): algorithm_globals.random_seed = 0 backend = self.backends_dict[backend_name] + expectation = ExpectationFactory.build( + operator=operator, + backend=backend, + ) trotter_qrte = TrotterQRTE(quantum_instance=backend, expectation=expectation) evolution_result = trotter_qrte.evolve(evolution_problem) - print(evolution_result.aux_ops_evaluated) + np.testing.assert_equal( evolution_result.evolved_state.eval(), expected_evolved_state ) + if backend_name == "qi_qasm": + expected_aux_ops_evaluated = expected_aux_ops_evaluated_qasm np.testing.assert_array_almost_equal( evolution_result.aux_ops_evaluated, expected_aux_ops_evaluated ) From 6e220a06f432fd78fa692d8f9e08b50e47323dfc Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 31 Mar 2022 19:18:04 +0200 Subject: [PATCH 121/145] Added param binding test. --- .../algorithms/evolvers/evolution_problem.py | 3 ++- .../real/trotterization/test_trotter_qrte.py | 27 ++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 535a81c4b8d8..83b3d3bbfdb1 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -36,7 +36,7 @@ def __init__( aux_operators: Optional[ListOrDict[OperatorBase]] = None, truncation_threshold: float = 1e-12, t_param: Optional[Parameter] = None, - hamiltonian_value_dict: Optional[Dict[Parameter, Union[complex]]] = None, + hamiltonian_value_dict: Optional[Dict[Parameter, complex]] = None, ): """ Args: @@ -153,6 +153,7 @@ def _check_parameters( ) params_set = t_param_set.union(hamiltonian_dict_param_set) hamiltonian_param_set = set(hamiltonian.parameters) + if hamiltonian_param_set != params_set: raise ValueError( f"Provided parameters {params_set} do not match Hamiltonian parameters " diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index 84b678994acd..989254e9710e 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -73,7 +73,6 @@ def setUp(self): self.backends_names = ["qi_qasm", "b_sv", "None", "qi_sv"] self.backends_names_not_none = ["qi_sv", "b_sv", "qi_qasm"] - # TODO add valid param binding Hamiltonian @data( ( None, @@ -186,8 +185,30 @@ def test_trotter_qrte_trotter_two_qubits(self, operator, expected_state): """Test for TrotterQRTE on two qubits with various types of a Hamiltonian.""" # LieTrotter with 1 rep initial_state = StateFn([1, 0, 0, 0]) - evolution_problem = EvolutionProblem(operator, 1, initial_state) + + for backend_name in self.backends_names: + with self.subTest(msg=f"Test {backend_name} backend."): + backend = self.backends_dict[backend_name] + trotter_qrte = TrotterQRTE(quantum_instance=backend) + evolution_result = trotter_qrte.evolve(evolution_problem) + np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) + + def test_trotter_qrte_trotter_two_qubits_with_params(self): + """Test for TrotterQRTE on two qubits with a parametrized Hamiltonian.""" + # LieTrotter with 1 rep + initial_state = StateFn([1, 0, 0, 0]) + w_param = Parameter("w") + u_param = Parameter("u") + params_dict = {w_param: 2.0, u_param: 3.0} + operator = w_param * (Z ^ Z) / 2.0 + (Z ^ I) + u_param * (I ^ Z) / 3.0 + time = 1 + evolution_problem = EvolutionProblem( + operator, time, initial_state, hamiltonian_value_dict=params_dict + ) + expected_state = VectorStateFn( + Statevector([-0.9899925 - 0.14112001j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], dims=(2, 2)) + ) for backend_name in self.backends_names: with self.subTest(msg=f"Test {backend_name} backend."): backend = self.backends_dict[backend_name] @@ -210,7 +231,7 @@ def test_trotter_qrte_trotter_two_qubits(self, operator, expected_state): ), ) @unpack - def test_trotter_qrte_qdrift_fractional_time(self, initial_state, expected_state): + def test_trotter_qrte_qdrift(self, initial_state, expected_state): """Test for TrotterQRTE with QDrift.""" operator = SummedOp([X, Z]) time = 1 From b1ce8e611bcfc83cf95125eb88158d376461e7c7 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 31 Mar 2022 19:25:29 +0200 Subject: [PATCH 122/145] Added missing setters. --- .../real/trotterization/trotter_qrte.py | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py index c6d891418605..8ae1279a51d0 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py @@ -90,6 +90,15 @@ def product_formula(self) -> ProductFormula: """Returns a product formula used in the algorithm.""" return self._product_formula + @product_formula.setter + def product_formula(self, product_formula) -> None: + """ + Sets a product formula. + Args: + product_formula: A formula that defines the Trotterization algorithm. + """ + self._product_formula = product_formula + @property def quantum_instance(self) -> Union[QuantumInstance, BaseBackend, Backend]: """Returns a quantum instance used in the algorithm.""" @@ -99,7 +108,8 @@ def quantum_instance(self) -> Union[QuantumInstance, BaseBackend, Backend]: def quantum_instance( self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] ) -> None: - """Sets a quantum instance and a circuit sampler. + """ + Sets a quantum instance and a circuit sampler. Args: quantum_instance: The quantum instance used to run this algorithm. """ @@ -117,6 +127,16 @@ def expectation(self) -> ExpectationBase: """Returns an expectation used in the algorithm.""" return self._expectation + @expectation.setter + def expectation(self, expectation: ExpectationBase) -> None: + """ + Sets an expectation. + Args: + expectation: An instance of ExpectationBase which defines a method for calculating + expectation values of EvolutionProblem.aux_operators. + """ + self._expectation = expectation + @classmethod def supports_aux_operators(cls) -> bool: """ From 0927d2a8422e82d3857ec475ee1987c4987911b3 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 31 Mar 2022 19:45:22 +0200 Subject: [PATCH 123/145] Updated unit tests. --- .../real/trotterization/test_trotter_qrte.py | 54 ++++++++----------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py index 989254e9710e..ed7e7208ea48 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py @@ -187,12 +187,9 @@ def test_trotter_qrte_trotter_two_qubits(self, operator, expected_state): initial_state = StateFn([1, 0, 0, 0]) evolution_problem = EvolutionProblem(operator, 1, initial_state) - for backend_name in self.backends_names: - with self.subTest(msg=f"Test {backend_name} backend."): - backend = self.backends_dict[backend_name] - trotter_qrte = TrotterQRTE(quantum_instance=backend) - evolution_result = trotter_qrte.evolve(evolution_problem) - np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) + trotter_qrte = TrotterQRTE() + evolution_result = trotter_qrte.evolve(evolution_problem) + np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) def test_trotter_qrte_trotter_two_qubits_with_params(self): """Test for TrotterQRTE on two qubits with a parametrized Hamiltonian.""" @@ -209,12 +206,9 @@ def test_trotter_qrte_trotter_two_qubits_with_params(self): expected_state = VectorStateFn( Statevector([-0.9899925 - 0.14112001j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], dims=(2, 2)) ) - for backend_name in self.backends_names: - with self.subTest(msg=f"Test {backend_name} backend."): - backend = self.backends_dict[backend_name] - trotter_qrte = TrotterQRTE(quantum_instance=backend) - evolution_result = trotter_qrte.evolve(evolution_problem) - np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) + trotter_qrte = TrotterQRTE() + evolution_result = trotter_qrte.evolve(evolution_problem) + np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) @data( ( @@ -237,13 +231,10 @@ def test_trotter_qrte_qdrift(self, initial_state, expected_state): time = 1 evolution_problem = EvolutionProblem(operator, time, initial_state) - for backend_name in self.backends_names: - with self.subTest(msg=f"Test {backend_name} backend."): - algorithm_globals.random_seed = 0 - backend = self.backends_dict[backend_name] - trotter_qrte = TrotterQRTE(quantum_instance=backend, product_formula=QDrift()) - evolution_result = trotter_qrte.evolve(evolution_problem) - np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) + algorithm_globals.random_seed = 0 + trotter_qrte = TrotterQRTE(product_formula=QDrift()) + evolution_result = trotter_qrte.evolve(evolution_problem) + np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) @data((Parameter("t"), {}), (None, {Parameter("t"): 2})) @unpack @@ -252,20 +243,17 @@ def test_trotter_qrte_trotter_errors(self, t_param, hamiltonian_value_dict): operator = X * Parameter("t") + Z initial_state = Zero time = 1 - for backend_name in self.backends_names: - with self.subTest(msg=f"Test {backend_name} backend."): - algorithm_globals.random_seed = 0 - backend = self.backends_dict[backend_name] - trotter_qrte = TrotterQRTE(quantum_instance=backend) - with assert_raises(ValueError): - evolution_problem = EvolutionProblem( - operator, - time, - initial_state, - t_param=t_param, - hamiltonian_value_dict=hamiltonian_value_dict, - ) - _ = trotter_qrte.evolve(evolution_problem) + algorithm_globals.random_seed = 0 + trotter_qrte = TrotterQRTE() + with assert_raises(ValueError): + evolution_problem = EvolutionProblem( + operator, + time, + initial_state, + t_param=t_param, + hamiltonian_value_dict=hamiltonian_value_dict, + ) + _ = trotter_qrte.evolve(evolution_problem) if __name__ == "__main__": From 519149d58358c71ad6f727f9314d9525010c516f Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Mon, 4 Apr 2022 12:44:34 +0200 Subject: [PATCH 124/145] Improved folder structure. --- qiskit/algorithms/__init__.py | 6 +++--- qiskit/algorithms/evolvers/imaginary/__init__.py | 11 ----------- .../evolvers/{imaginary => }/imaginary_evolver.py | 2 +- qiskit/algorithms/evolvers/real/__init__.py | 11 ----------- qiskit/algorithms/evolvers/{real => }/real_evolver.py | 2 +- .../evolvers/{real => }/trotterization/__init__.py | 9 ++++----- .../{real => }/trotterization/trotter_qrte.py | 4 +--- .../evolvers/real/trotterization/__init__.py | 11 ----------- .../evolvers/{real => trotterization}/__init__.py | 0 .../{real => }/trotterization/test_trotter_qrte.py | 2 +- 10 files changed, 11 insertions(+), 47 deletions(-) delete mode 100644 qiskit/algorithms/evolvers/imaginary/__init__.py rename qiskit/algorithms/evolvers/{imaginary => }/imaginary_evolver.py (92%) delete mode 100644 qiskit/algorithms/evolvers/real/__init__.py rename qiskit/algorithms/evolvers/{real => }/real_evolver.py (92%) rename qiskit/algorithms/evolvers/{real => }/trotterization/__init__.py (67%) rename qiskit/algorithms/evolvers/{real => }/trotterization/trotter_qrte.py (98%) delete mode 100644 test/python/algorithms/evolvers/real/trotterization/__init__.py rename test/python/algorithms/evolvers/{real => trotterization}/__init__.py (100%) rename test/python/algorithms/evolvers/{real => }/trotterization/test_trotter_qrte.py (99%) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index b6908c1c0ef7..3e34366c5919 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -206,8 +206,8 @@ from .algorithm_result import AlgorithmResult from .evolvers import EvolutionResult, EvolutionProblem -from .evolvers.real.real_evolver import RealEvolver -from .evolvers.imaginary.imaginary_evolver import ImaginaryEvolver +from qiskit.algorithms.evolvers.real_evolver import RealEvolver +from qiskit.algorithms.evolvers.imaginary_evolver import ImaginaryEvolver from .variational_algorithm import VariationalAlgorithm, VariationalResult from .amplitude_amplifiers import Grover, GroverResult, AmplificationProblem, AmplitudeAmplifier from .amplitude_estimators import ( @@ -244,7 +244,7 @@ ) from .exceptions import AlgorithmError from .aux_ops_evaluator import eval_observables -from .evolvers.real.trotterization import TrotterQRTE +from qiskit.algorithms.evolvers.trotterization import TrotterQRTE __all__ = [ "AlgorithmResult", diff --git a/qiskit/algorithms/evolvers/imaginary/__init__.py b/qiskit/algorithms/evolvers/imaginary/__init__.py deleted file mode 100644 index b3ac36d2a6d9..000000000000 --- a/qiskit/algorithms/evolvers/imaginary/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 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. diff --git a/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py b/qiskit/algorithms/evolvers/imaginary_evolver.py similarity index 92% rename from qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py rename to qiskit/algorithms/evolvers/imaginary_evolver.py index 4b1ab96eecdf..5a9c6aad462d 100644 --- a/qiskit/algorithms/evolvers/imaginary/imaginary_evolver.py +++ b/qiskit/algorithms/evolvers/imaginary_evolver.py @@ -14,7 +14,7 @@ from abc import ABC -from ..evolver import Evolver +from qiskit.algorithms.evolvers.evolver import Evolver class ImaginaryEvolver(Evolver, ABC): diff --git a/qiskit/algorithms/evolvers/real/__init__.py b/qiskit/algorithms/evolvers/real/__init__.py deleted file mode 100644 index b3ac36d2a6d9..000000000000 --- a/qiskit/algorithms/evolvers/real/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 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. diff --git a/qiskit/algorithms/evolvers/real/real_evolver.py b/qiskit/algorithms/evolvers/real_evolver.py similarity index 92% rename from qiskit/algorithms/evolvers/real/real_evolver.py rename to qiskit/algorithms/evolvers/real_evolver.py index c1663ed3868c..c44a58f8f817 100644 --- a/qiskit/algorithms/evolvers/real/real_evolver.py +++ b/qiskit/algorithms/evolvers/real_evolver.py @@ -14,7 +14,7 @@ from abc import ABC -from ..evolver import Evolver +from qiskit.algorithms.evolvers.evolver import Evolver class RealEvolver(Evolver, ABC): diff --git a/qiskit/algorithms/evolvers/real/trotterization/__init__.py b/qiskit/algorithms/evolvers/trotterization/__init__.py similarity index 67% rename from qiskit/algorithms/evolvers/real/trotterization/__init__.py rename to qiskit/algorithms/evolvers/trotterization/__init__.py index 288a55a520a0..9567c57c9b1b 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/__init__.py +++ b/qiskit/algorithms/evolvers/trotterization/__init__.py @@ -10,12 +10,11 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """This package contains Trotterization-based Quantum Real Time Evolution algorithm. -It is compliant with the new Quantum Time Evolution Framework and makes use of `ProductFormula` and -`PauliEvolutionGate` implementations. -The evolution with gradients assumes that a Hamiltonian is a linear combination of `PauliOp` objects -w.r.t. given parameters. It case of a single summand, it might be a `PauliOp`.""" +It is compliant with the new Quantum Time Evolution Framework and makes use of +:class:`qiskit.synthesis.evolution.ProductFormula` and +:class:`~qiskit.circuit.library.PauliEvolutionGate` implementations. """ -from qiskit.algorithms.evolvers.real.trotterization.trotter_qrte import ( +from qiskit.algorithms.evolvers.trotterization.trotter_qrte import ( TrotterQRTE, ) diff --git a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py similarity index 98% rename from qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py rename to qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index 8ae1279a51d0..63f9565c7792 100644 --- a/qiskit/algorithms/evolvers/real/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -17,7 +17,7 @@ from qiskit import QuantumCircuit from qiskit.algorithms.aux_ops_evaluator import eval_observables from qiskit.algorithms.evolvers import EvolutionProblem, EvolutionResult -from qiskit.algorithms.evolvers.real.real_evolver import RealEvolver +from qiskit.algorithms.evolvers.real_evolver import RealEvolver from qiskit.opflow import ( SummedOp, PauliOp, @@ -26,8 +26,6 @@ CircuitSampler, PauliSumOp, StateFn, - ListOp, - CircuitStateFn, OperatorBase, ) from qiskit.circuit.library import PauliEvolutionGate diff --git a/test/python/algorithms/evolvers/real/trotterization/__init__.py b/test/python/algorithms/evolvers/real/trotterization/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/test/python/algorithms/evolvers/real/trotterization/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. diff --git a/test/python/algorithms/evolvers/real/__init__.py b/test/python/algorithms/evolvers/trotterization/__init__.py similarity index 100% rename from test/python/algorithms/evolvers/real/__init__.py rename to test/python/algorithms/evolvers/trotterization/__init__.py diff --git a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py similarity index 99% rename from test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py rename to test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py index ed7e7208ea48..42ec7733a924 100644 --- a/test/python/algorithms/evolvers/real/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py @@ -21,7 +21,7 @@ from qiskit import BasicAer, QuantumCircuit from qiskit.algorithms import EvolutionProblem -from qiskit.algorithms.evolvers.real.trotterization.trotter_qrte import ( +from qiskit.algorithms.evolvers.trotterization import ( TrotterQRTE, ) from qiskit.quantum_info import Statevector, SparsePauliOp, Pauli, PauliTable From 4211c303a3f631f304740e4cac4dbe5f0ce4ba61 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 7 Apr 2022 11:44:11 -0400 Subject: [PATCH 125/145] Updated interfaces. --- qiskit/algorithms/evolvers/imaginary_evolver.py | 4 ++-- qiskit/algorithms/evolvers/real_evolver.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/algorithms/evolvers/imaginary_evolver.py b/qiskit/algorithms/evolvers/imaginary_evolver.py index 7743597c1e31..309bb73b08af 100644 --- a/qiskit/algorithms/evolvers/imaginary_evolver.py +++ b/qiskit/algorithms/evolvers/imaginary_evolver.py @@ -14,8 +14,8 @@ from abc import ABC, abstractmethod -from ..evolution_problem import EvolutionProblem -from ..evolution_result import EvolutionResult +from .evolution_problem import EvolutionProblem +from .evolution_result import EvolutionResult class ImaginaryEvolver(ABC): diff --git a/qiskit/algorithms/evolvers/real_evolver.py b/qiskit/algorithms/evolvers/real_evolver.py index b6c2f2b989dc..6107facfe542 100644 --- a/qiskit/algorithms/evolvers/real_evolver.py +++ b/qiskit/algorithms/evolvers/real_evolver.py @@ -14,8 +14,8 @@ from abc import ABC, abstractmethod -from ..evolution_problem import EvolutionProblem -from ..evolution_result import EvolutionResult +from .evolution_problem import EvolutionProblem +from .evolution_result import EvolutionResult class RealEvolver(ABC): From 3242933f423906e28d966686f59a994044a3be88 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 7 Apr 2022 12:08:37 -0400 Subject: [PATCH 126/145] Fix lint. --- qiskit/algorithms/__init__.py | 6 +++--- qiskit/algorithms/evolvers/trotterization/trotter_qrte.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index 3e34366c5919..d9cdfa036a3d 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -206,8 +206,8 @@ from .algorithm_result import AlgorithmResult from .evolvers import EvolutionResult, EvolutionProblem -from qiskit.algorithms.evolvers.real_evolver import RealEvolver -from qiskit.algorithms.evolvers.imaginary_evolver import ImaginaryEvolver +from .evolvers.real_evolver import RealEvolver +from .evolvers.imaginary_evolver import ImaginaryEvolver from .variational_algorithm import VariationalAlgorithm, VariationalResult from .amplitude_amplifiers import Grover, GroverResult, AmplificationProblem, AmplitudeAmplifier from .amplitude_estimators import ( @@ -244,7 +244,7 @@ ) from .exceptions import AlgorithmError from .aux_ops_evaluator import eval_observables -from qiskit.algorithms.evolvers.trotterization import TrotterQRTE +from .evolvers.trotterization import TrotterQRTE __all__ = [ "AlgorithmResult", diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index 63f9565c7792..ad0a9de16ae7 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -89,7 +89,7 @@ def product_formula(self) -> ProductFormula: return self._product_formula @product_formula.setter - def product_formula(self, product_formula) -> None: + def product_formula(self, product_formula: ProductFormula) -> None: """ Sets a product formula. Args: From 3831c9562f4bc624467cd9d6c9d4f548f1e2cab1 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 8 Apr 2022 09:38:42 -0400 Subject: [PATCH 127/145] Removed legacy (soon) BaseBackend. --- .../evolvers/trotterization/trotter_qrte.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index ad0a9de16ae7..af649583d15b 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -29,7 +29,7 @@ OperatorBase, ) from qiskit.circuit.library import PauliEvolutionGate -from qiskit.providers import Backend, BaseBackend +from qiskit.providers import Backend from qiskit.quantum_info import SparsePauliOp, Pauli from qiskit.synthesis import ProductFormula, LieTrotter from qiskit.utils import QuantumInstance @@ -63,7 +63,7 @@ def __init__( self, product_formula: Optional[ProductFormula] = None, expectation: Optional[ExpectationBase] = None, - quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None, + quantum_instance: Optional[Union[QuantumInstance, Backend]] = None, ) -> None: """ Args: @@ -98,20 +98,18 @@ def product_formula(self, product_formula: ProductFormula) -> None: self._product_formula = product_formula @property - def quantum_instance(self) -> Union[QuantumInstance, BaseBackend, Backend]: + def quantum_instance(self) -> Union[QuantumInstance, Backend]: """Returns a quantum instance used in the algorithm.""" return self._quantum_instance @quantum_instance.setter - def quantum_instance( - self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] - ) -> None: + def quantum_instance(self, quantum_instance: Union[QuantumInstance, Backend]) -> None: """ Sets a quantum instance and a circuit sampler. Args: quantum_instance: The quantum instance used to run this algorithm. """ - if isinstance(quantum_instance, (BaseBackend, Backend)): + if isinstance(quantum_instance, (Backend)): quantum_instance = QuantumInstance(quantum_instance) self._circuit_sampler = None From ff03a162833c5db34529a0e53e770b5c02be3fcf Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Fri, 8 Apr 2022 13:30:14 -0400 Subject: [PATCH 128/145] Updated copyright years. --- qiskit/algorithms/evolvers/trotterization/__init__.py | 2 +- qiskit/algorithms/evolvers/trotterization/trotter_qrte.py | 2 +- .../algorithms/evolvers/trotterization/test_trotter_qrte.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/algorithms/evolvers/trotterization/__init__.py b/qiskit/algorithms/evolvers/trotterization/__init__.py index 9567c57c9b1b..fe1b8d8aedf2 100644 --- a/qiskit/algorithms/evolvers/trotterization/__init__.py +++ b/qiskit/algorithms/evolvers/trotterization/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 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 diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index af649583d15b..b8dd90670b62 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 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 diff --git a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py index 42ec7733a924..08ca78c42d5a 100644 --- a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 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 From d3c35e65be53825f81dee2d0db0e5f39a93d9b6d Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 12 Apr 2022 18:26:42 -0400 Subject: [PATCH 129/145] Added example to reno. --- .../notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml b/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml index 00e344c66017..7a9245c94f2b 100644 --- a/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml +++ b/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml @@ -5,3 +5,13 @@ features: :class:`qiskit.algorithms.TrotterQRTE`. It is compliant with the new Quantum Time Evolution Framework and makes use of :class:`qiskit.synthesis.evolution.ProductFormula` and :class:`qiskit.circuit.library.PauliEvolutionGate` implementations. + + .. code-block:: python + operator = SummedOp([X, Z]) + initial_state = StateFn([1, 0]) + time = 1 + evolution_problem = EvolutionProblem(operator, time, initial_state) + + trotter_qrte = TrotterQRTE() + evolution_result = trotter_qrte.evolve(evolution_problem) + evolved_state_circuit = evolution_result.evolved_state From 50840f8091d1099c8749d4511902690c77ca95ad Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Thu, 14 Apr 2022 10:29:30 -0400 Subject: [PATCH 130/145] Fixed reno. --- .../notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml b/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml index 7a9245c94f2b..5746ab17bcff 100644 --- a/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml +++ b/releasenotes/notes/feature-trotter-qrte-f7b28c4fd4b361d2.yaml @@ -7,6 +7,17 @@ features: :class:`qiskit.circuit.library.PauliEvolutionGate` implementations. .. code-block:: python + + from qiskit.algorithms import EvolutionProblem + from qiskit.algorithms.evolvers.trotterization import ( + TrotterQRTE, + ) + from qiskit.opflow import ( + X, + Z, + StateFn, + SummedOp, + ) operator = SummedOp([X, Z]) initial_state = StateFn([1, 0]) time = 1 From 62c97888062853fe8fd017a6ad569286c97f6e62 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 26 Apr 2022 13:34:58 +0200 Subject: [PATCH 131/145] Removed BaseOperator. --- .../algorithms/evolvers/evolution_problem.py | 21 ++++--------------- .../evolvers/trotterization/trotter_qrte.py | 2 +- .../evolvers/test_evolution_problem.py | 4 ++-- .../trotterization/test_trotter_qrte.py | 19 +---------------- 4 files changed, 8 insertions(+), 38 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 83b3d3bbfdb1..278eac56e8b6 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -18,7 +18,6 @@ from qiskit.circuit import Parameter from qiskit.opflow import OperatorBase, StateFn from ..list_or_dict import ListOrDict -from ...quantum_info.operators.base_operator import BaseOperator class EvolutionProblem: @@ -30,7 +29,7 @@ class EvolutionProblem: def __init__( self, - hamiltonian: Union[OperatorBase, BaseOperator], + hamiltonian: OperatorBase, time: float, initial_state: Union[StateFn, QuantumCircuit], aux_operators: Optional[ListOrDict[OperatorBase]] = None, @@ -67,22 +66,16 @@ def __init__( self.truncation_threshold = truncation_threshold @property - def hamiltonian(self) -> Union[OperatorBase, BaseOperator]: + def hamiltonian(self) -> OperatorBase: """Returns a hamiltonian.""" return self._hamiltonian @hamiltonian.setter - def hamiltonian(self, hamiltonian: Union[OperatorBase, BaseOperator]) -> None: + def hamiltonian(self, hamiltonian: OperatorBase) -> None: """ Sets a hamiltonian and validates it. - Raises: - ValueError: If no Hamiltonian is provided. """ - if hamiltonian is None: - raise ValueError( - "No ``hamiltonian`` provided for the EvolutionProblem. It is required." - ) self._check_parameters(hamiltonian, self.hamiltonian_value_dict, self.t_param) self._hamiltonian = hamiltonian @@ -114,18 +107,12 @@ def initial_state(self, initial_state: Union[StateFn, QuantumCircuit]) -> None: """ Sets an initial state and validates it. - Raises: - ValueError: If no initial state is provided. """ - if initial_state is None: - raise ValueError( - "No ``initial_state`` provided for the EvolutionProblem. It is required." - ) self._initial_state = initial_state def _check_parameters( self, - hamiltonian: Union[OperatorBase, BaseOperator], + hamiltonian: OperatorBase, hamiltonian_value_dict: Optional[Dict[Parameter, Union[complex]]] = None, t_param: Optional[Parameter] = None, ) -> None: diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index b8dd90670b62..70b332d59fd8 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -109,7 +109,7 @@ def quantum_instance(self, quantum_instance: Union[QuantumInstance, Backend]) -> Args: quantum_instance: The quantum instance used to run this algorithm. """ - if isinstance(quantum_instance, (Backend)): + if isinstance(quantum_instance, Backend): quantum_instance = QuantumInstance(quantum_instance) self._circuit_sampler = None diff --git a/test/python/algorithms/evolvers/test_evolution_problem.py b/test/python/algorithms/evolvers/test_evolution_problem.py index 5a6b93dcc6b3..85f61bfa7d20 100644 --- a/test/python/algorithms/evolvers/test_evolution_problem.py +++ b/test/python/algorithms/evolvers/test_evolution_problem.py @@ -18,7 +18,7 @@ from qiskit.algorithms.evolvers.evolution_problem import EvolutionProblem from qiskit.circuit import Parameter -from qiskit.opflow import Y, Z, One, X, Zero +from qiskit.opflow import Y, Z, One, X @ddt @@ -79,7 +79,7 @@ def test_init_all(self): self.assertEqual(evo_problem.t_param, expected_t_param) self.assertEqual(evo_problem.hamiltonian_value_dict, expected_hamiltonian_value_dict) - @data([Y, -1, One], [Y, -1.2, One], [Y, 0, One], [None, 1, Zero], [Y, 2.5, None]) + @data([Y, -1, One], [Y, -1.2, One], [Y, 0, One]) @unpack def test_init_errors(self, hamiltonian, time, initial_state): """Tests expected errors are thrown on invalid input arguments.""" diff --git a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py index 08ca78c42d5a..e876ecdfc2e1 100644 --- a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py @@ -24,7 +24,7 @@ from qiskit.algorithms.evolvers.trotterization import ( TrotterQRTE, ) -from qiskit.quantum_info import Statevector, SparsePauliOp, Pauli, PauliTable +from qiskit.quantum_info import Statevector, Pauli from qiskit.utils import algorithm_globals, QuantumInstance from qiskit.circuit import Parameter from qiskit.opflow import ( @@ -162,23 +162,6 @@ def test_trotter_qrte_trotter_single_qubit_aux_ops(self): ) ), ), - ( - SparsePauliOp(Pauli("XI")), - VectorStateFn( - Statevector( - [0.54030231 + 0.0j, 0.0 + 0.0j, 0.0 - 0.84147098j, 0.0 + 0.0j], dims=(2, 2) - ) - ), - ), - ( - SparsePauliOp(PauliTable.from_labels(["XX", "ZZ"])), - VectorStateFn( - Statevector( - [0.29192658 - 0.45464871j, 0.0 + 0.0j, 0.0 + 0.0j, -0.70807342 - 0.45464871j], - dims=(2, 2), - ) - ), - ), ) @unpack def test_trotter_qrte_trotter_two_qubits(self, operator, expected_state): From 8f3f98140be122c4df72a3b3b34a525c9b74ada0 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 26 Apr 2022 13:39:13 +0200 Subject: [PATCH 132/145] Code refactoring. --- .../algorithms/evolvers/trotterization/test_trotter_qrte.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py index e876ecdfc2e1..b52a5f5d51ab 100644 --- a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py @@ -14,6 +14,7 @@ import unittest +from qiskit.circuit.library import ZGate from test.python.opflow import QiskitOpflowTestCase from ddt import ddt, data, unpack import numpy as np @@ -24,7 +25,7 @@ from qiskit.algorithms.evolvers.trotterization import ( TrotterQRTE, ) -from qiskit.quantum_info import Statevector, Pauli +from qiskit.quantum_info import Statevector from qiskit.utils import algorithm_globals, QuantumInstance from qiskit.circuit import Parameter from qiskit.opflow import ( @@ -201,7 +202,7 @@ def test_trotter_qrte_trotter_two_qubits_with_params(self): ), ), ( - QuantumCircuit(1).compose(Pauli("Z").to_instruction(), [0]), + QuantumCircuit(1).compose(ZGate(), [0]), VectorStateFn( Statevector([0.23071786 - 0.69436148j, 0.4646314 - 0.49874749j], dims=(2,)) ), From 5eba3e5cb283651dac61a80a5a774efdaada2749 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 26 Apr 2022 14:06:33 +0200 Subject: [PATCH 133/145] Imports fix. --- .../algorithms/evolvers/trotterization/test_trotter_qrte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py index b52a5f5d51ab..89f91001a756 100644 --- a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py @@ -14,7 +14,6 @@ import unittest -from qiskit.circuit.library import ZGate from test.python.opflow import QiskitOpflowTestCase from ddt import ddt, data, unpack import numpy as np @@ -25,6 +24,7 @@ from qiskit.algorithms.evolvers.trotterization import ( TrotterQRTE, ) +from qiskit.circuit.library import ZGate from qiskit.quantum_info import Statevector from qiskit.utils import algorithm_globals, QuantumInstance from qiskit.circuit import Parameter From 1f459bbac18273bd54d5f78f0ff2caa49b9fc3fd Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 26 Apr 2022 14:38:50 +0200 Subject: [PATCH 134/145] Delayed parameters checks. --- .../algorithms/evolvers/evolution_problem.py | 27 ++++---------- .../evolvers/trotterization/trotter_qrte.py | 1 + .../evolvers/test_evolution_problem.py | 35 +++++++++++++++++-- .../trotterization/test_trotter_qrte.py | 2 +- 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 278eac56e8b6..c41d3af889ab 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -76,8 +76,6 @@ def hamiltonian(self, hamiltonian: OperatorBase) -> None: Sets a hamiltonian and validates it. """ - - self._check_parameters(hamiltonian, self.hamiltonian_value_dict, self.t_param) self._hamiltonian = hamiltonian @property @@ -110,36 +108,25 @@ def initial_state(self, initial_state: Union[StateFn, QuantumCircuit]) -> None: """ self._initial_state = initial_state - def _check_parameters( - self, - hamiltonian: OperatorBase, - hamiltonian_value_dict: Optional[Dict[Parameter, Union[complex]]] = None, - t_param: Optional[Parameter] = None, - ) -> None: + def validate_params(self) -> None: """ Checks if all parameters present in the Hamiltonian are also present in the dictionary that maps them to values. - Args: - hamiltonian: The Hamiltonian under which to evolve the system. - hamiltonian_value_dict: If the Hamiltonian contains free parameters, this - dictionary maps all these parameters to values. - t_param: Time parameter in case of a time-dependent Hamiltonian. This - free parameter must be within the ``hamiltonian``. Raises: ValueError: If Hamiltonian parameters cannot be bound with data provided. """ - if isinstance(hamiltonian, OperatorBase): + if isinstance(self.hamiltonian, OperatorBase): t_param_set = set() - if t_param is not None: - t_param_set.add(t_param) + if self.t_param is not None: + t_param_set.add(self.t_param) hamiltonian_dict_param_set = set() - if hamiltonian_value_dict is not None: + if self.hamiltonian_value_dict is not None: hamiltonian_dict_param_set = hamiltonian_dict_param_set.union( - set(hamiltonian_value_dict.keys()) + set(self.hamiltonian_value_dict.keys()) ) params_set = t_param_set.union(hamiltonian_dict_param_set) - hamiltonian_param_set = set(hamiltonian.parameters) + hamiltonian_param_set = set(self.hamiltonian.parameters) if hamiltonian_param_set != params_set: raise ValueError( diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index 70b332d59fd8..066441005c39 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -168,6 +168,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: currently supported). ValueError: If the ``initial_state`` is not provided in the EvolutionProblem. """ + evolution_problem.validate_params() if evolution_problem.t_param is not None: raise ValueError( "TrotterQRTE does not accept a time dependent hamiltonian," diff --git a/test/python/algorithms/evolvers/test_evolution_problem.py b/test/python/algorithms/evolvers/test_evolution_problem.py index 85f61bfa7d20..0d1d18951039 100644 --- a/test/python/algorithms/evolvers/test_evolution_problem.py +++ b/test/python/algorithms/evolvers/test_evolution_problem.py @@ -18,7 +18,7 @@ from qiskit.algorithms.evolvers.evolution_problem import EvolutionProblem from qiskit.circuit import Parameter -from qiskit.opflow import Y, Z, One, X +from qiskit.opflow import Y, Z, One, X, Zero @ddt @@ -82,10 +82,41 @@ def test_init_all(self): @data([Y, -1, One], [Y, -1.2, One], [Y, 0, One]) @unpack def test_init_errors(self, hamiltonian, time, initial_state): - """Tests expected errors are thrown on invalid input arguments.""" + """Tests expected errors are thrown on invalid time argument.""" with assert_raises(ValueError): _ = EvolutionProblem(hamiltonian, time, initial_state) + def test_validate_params(self): + """Tests expected errors are thrown on parameters mismatch.""" + param_x = Parameter("x") + param_y = Parameter("y") + with self.subTest(msg="Parameter missing in dict."): + hamiltonian = param_x * X + param_y * Y + param_dict = {param_y: 2} + evolution_problem = EvolutionProblem( + hamiltonian, 2, Zero, hamiltonian_value_dict=param_dict + ) + with assert_raises(ValueError): + evolution_problem.validate_params() + + with self.subTest(msg="Empty dict."): + hamiltonian = param_x * X + param_y * Y + param_dict = {} + evolution_problem = EvolutionProblem( + hamiltonian, 2, Zero, hamiltonian_value_dict=param_dict + ) + with assert_raises(ValueError): + evolution_problem.validate_params() + + with self.subTest(msg="Extra parameter in dict."): + hamiltonian = param_x * X + param_y * Y + param_dict = {param_y: 2, param_x: 1, Parameter("z"): 1} + evolution_problem = EvolutionProblem( + hamiltonian, 2, Zero, hamiltonian_value_dict=param_dict + ) + with assert_raises(ValueError): + evolution_problem.validate_params() + if __name__ == "__main__": unittest.main() diff --git a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py index 89f91001a756..7baad84fe59b 100644 --- a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py +++ b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py @@ -220,7 +220,7 @@ def test_trotter_qrte_qdrift(self, initial_state, expected_state): evolution_result = trotter_qrte.evolve(evolution_problem) np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) - @data((Parameter("t"), {}), (None, {Parameter("t"): 2})) + @data((Parameter("t"), {}), (None, {Parameter("x"): 2}), (None, None)) @unpack def test_trotter_qrte_trotter_errors(self, t_param, hamiltonian_value_dict): """Test TrotterQRTE with raising errors.""" From 68a436ecd0edccccfb105580fce53a1e4662233f Mon Sep 17 00:00:00 2001 From: dlasecki Date: Tue, 26 Apr 2022 17:13:11 +0200 Subject: [PATCH 135/145] Update qiskit/algorithms/evolvers/evolution_problem.py Co-authored-by: Julien Gacon --- qiskit/algorithms/evolvers/evolution_problem.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index c41d3af889ab..0ce7fc96e685 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -72,10 +72,7 @@ def hamiltonian(self) -> OperatorBase: @hamiltonian.setter def hamiltonian(self, hamiltonian: OperatorBase) -> None: - """ - Sets a hamiltonian and validates it. - - """ + """Sets a hamiltonian.""" self._hamiltonian = hamiltonian @property From c5561cfc6f3979a1ad3bff8e6824965a07c11072 Mon Sep 17 00:00:00 2001 From: Dariusz Lasecki Date: Tue, 26 Apr 2022 17:19:53 +0200 Subject: [PATCH 136/145] Code refactoring. --- .../algorithms/evolvers/evolution_problem.py | 23 ------------------- .../evolvers/trotterization/trotter_qrte.py | 12 ++++------ 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 0ce7fc96e685..36255b42e4bc 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -65,16 +65,6 @@ def __init__( self.aux_operators = aux_operators self.truncation_threshold = truncation_threshold - @property - def hamiltonian(self) -> OperatorBase: - """Returns a hamiltonian.""" - return self._hamiltonian - - @hamiltonian.setter - def hamiltonian(self, hamiltonian: OperatorBase) -> None: - """Sets a hamiltonian.""" - self._hamiltonian = hamiltonian - @property def time(self) -> float: """Returns time.""" @@ -92,19 +82,6 @@ def time(self, time: float) -> None: raise ValueError(f"Evolution time must be > 0 but was {time}.") self._time = time - @property - def initial_state(self) -> Union[StateFn, QuantumCircuit]: - """Returns an initial state.""" - return self._initial_state - - @initial_state.setter - def initial_state(self, initial_state: Union[StateFn, QuantumCircuit]) -> None: - """ - Sets an initial state and validates it. - - """ - self._initial_state = initial_state - def validate_params(self) -> None: """ Checks if all parameters present in the Hamiltonian are also present in the dictionary diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index 066441005c39..9eae355f1ff1 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -30,7 +30,6 @@ ) from qiskit.circuit.library import PauliEvolutionGate from qiskit.providers import Backend -from qiskit.quantum_info import SparsePauliOp, Pauli from qiskit.synthesis import ProductFormula, LieTrotter from qiskit.utils import QuantumInstance @@ -156,8 +155,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: Args: evolution_problem: Instance defining evolution problem. For the included Hamiltonian, - ``PauliOp``, ``SparsePauliOp``, ``Pauli`` or ``SummedOp`` thereof or ``PauliSumOp`` - are supported by TrotterQRTE. + ``PauliOp``, ``SummedOp`` or ``PauliSumOp`` are supported by TrotterQRTE. Returns: Evolution result that includes an evolved state as a quantum circuit and, optionally, @@ -183,9 +181,9 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: "``quantum_instance`` was provided." ) hamiltonian = evolution_problem.hamiltonian - if not isinstance(hamiltonian, (Pauli, PauliOp, SparsePauliOp, PauliSumOp, SummedOp)): + if not isinstance(hamiltonian, (PauliOp, PauliSumOp, SummedOp)): raise ValueError( - f"TrotterQRTE only accepts Pauli | PauliOp | SparsePauliOp | " + f"TrotterQRTE only accepts PauliOp | " f"PauliSumOp | SummedOp, {type(hamiltonian)} provided." ) if isinstance(hamiltonian, OperatorBase): @@ -238,9 +236,9 @@ def _summed_op_to_pauli_sum_op( # we need to convert it into a PauliSumOp for the PauliEvolutionGate. op_list = [] for op in hamiltonian.oplist: - if not isinstance(op, (PauliOp, Pauli, SparsePauliOp)): + if not isinstance(op, PauliOp): raise ValueError( - f"Content of the Hamiltonian not of type PauliOp, Pauli, or SparsePauliOp. The " + f"Content of the Hamiltonian not of type PauliOp. The " f"following type detected: {type(op)}." ) op_list.append(op) From 07d06de3ec9e47d78301b18d246e1c9132ee5980 Mon Sep 17 00:00:00 2001 From: dlasecki Date: Tue, 26 Apr 2022 17:30:43 +0200 Subject: [PATCH 137/145] Update qiskit/algorithms/evolvers/evolution_problem.py Co-authored-by: Julien Gacon --- qiskit/algorithms/evolvers/evolution_problem.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py index 36255b42e4bc..b069effe1747 100644 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ b/qiskit/algorithms/evolvers/evolution_problem.py @@ -53,8 +53,6 @@ def __init__( Raises: ValueError: If non-positive time of evolution is provided. - ValueError: If no ``initial_state`` is provided. - ValueError: If not all parameter values are provided. """ self.t_param = t_param From 40dde49c582d6edb86f60dba546d16ef1212b19a Mon Sep 17 00:00:00 2001 From: dlasecki Date: Tue, 26 Apr 2022 23:26:54 +0200 Subject: [PATCH 138/145] Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon --- qiskit/algorithms/evolvers/trotterization/trotter_qrte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index 9eae355f1ff1..cef5148d8c6a 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -218,7 +218,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: @staticmethod def _summed_op_to_pauli_sum_op( - hamiltonian: Union[SummedOp], + hamiltonian: SummedOp, ) -> Union[PauliSumOp, PauliOp]: """ Tries binding parameters in a Hamiltonian. From 3d2f164f477501dbe0f986be6cf898e3ebb4da18 Mon Sep 17 00:00:00 2001 From: dlasecki Date: Tue, 26 Apr 2022 23:27:08 +0200 Subject: [PATCH 139/145] Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon --- qiskit/algorithms/evolvers/trotterization/trotter_qrte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index cef5148d8c6a..87c36fb6e16c 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -224,7 +224,7 @@ def _summed_op_to_pauli_sum_op( Tries binding parameters in a Hamiltonian. Args: - hamiltonian: The Hamiltonian of that defines an evolution. + hamiltonian: The Hamiltonian that defines an evolution. Returns: Hamiltonian. From 17bf7e945dcefe5c5c96b4880cafed0cd2fe7bc8 Mon Sep 17 00:00:00 2001 From: dlasecki Date: Tue, 26 Apr 2022 23:27:22 +0200 Subject: [PATCH 140/145] Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon --- qiskit/algorithms/evolvers/trotterization/trotter_qrte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index 87c36fb6e16c..bbb7d2676f72 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -177,7 +177,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: self._quantum_instance is None or self._expectation is None ): raise ValueError( - "aux_operators where provided for evaluations but no ``expectation`` or " + "aux_operators were provided for evaluations but no ``expectation`` or " "``quantum_instance`` was provided." ) hamiltonian = evolution_problem.hamiltonian From 2c3fc90ad058c13c4b87bb6e0d3b7565f5d1746a Mon Sep 17 00:00:00 2001 From: dlasecki Date: Tue, 26 Apr 2022 23:29:02 +0200 Subject: [PATCH 141/145] Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon --- qiskit/algorithms/evolvers/trotterization/trotter_qrte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index bbb7d2676f72..9aa8dc8ac347 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -183,7 +183,7 @@ def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: hamiltonian = evolution_problem.hamiltonian if not isinstance(hamiltonian, (PauliOp, PauliSumOp, SummedOp)): raise ValueError( - f"TrotterQRTE only accepts PauliOp | " + "TrotterQRTE only accepts PauliOp | " f"PauliSumOp | SummedOp, {type(hamiltonian)} provided." ) if isinstance(hamiltonian, OperatorBase): From d976453d9c5649a8288a318ff4782c86146b537d Mon Sep 17 00:00:00 2001 From: dlasecki Date: Tue, 26 Apr 2022 23:29:16 +0200 Subject: [PATCH 142/145] Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon --- qiskit/algorithms/evolvers/trotterization/trotter_qrte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index 9aa8dc8ac347..33c6b5da411d 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -238,7 +238,7 @@ def _summed_op_to_pauli_sum_op( for op in hamiltonian.oplist: if not isinstance(op, PauliOp): raise ValueError( - f"Content of the Hamiltonian not of type PauliOp. The " + "Content of the Hamiltonian not of type PauliOp. The " f"following type detected: {type(op)}." ) op_list.append(op) From 6e68f4d32c5afd9a2cac61bae15827e29743fefd Mon Sep 17 00:00:00 2001 From: dlasecki Date: Tue, 26 Apr 2022 23:30:10 +0200 Subject: [PATCH 143/145] Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon --- qiskit/algorithms/evolvers/trotterization/trotter_qrte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index 33c6b5da411d..1e850de9e70a 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -97,7 +97,7 @@ def product_formula(self, product_formula: ProductFormula) -> None: self._product_formula = product_formula @property - def quantum_instance(self) -> Union[QuantumInstance, Backend]: + def quantum_instance(self) -> Optional[QuantumInstance]: """Returns a quantum instance used in the algorithm.""" return self._quantum_instance From a78dfa2714d2a4f8121773ab6bc31079692c4780 Mon Sep 17 00:00:00 2001 From: dlasecki Date: Tue, 26 Apr 2022 23:31:44 +0200 Subject: [PATCH 144/145] Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon --- qiskit/algorithms/evolvers/trotterization/trotter_qrte.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index 1e850de9e70a..3885ef966bfa 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -102,7 +102,7 @@ def quantum_instance(self) -> Optional[QuantumInstance]: return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, Backend]) -> None: + def quantum_instance(self, quantum_instance: Optional[Union[QuantumInstance, Backend]]) -> None: """ Sets a quantum instance and a circuit sampler. Args: From ed40a5e8013bcbb1516dbcbe7d3334225d67750a Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Wed, 27 Apr 2022 09:26:52 +0200 Subject: [PATCH 145/145] Expectation might be None --- qiskit/algorithms/evolvers/trotterization/trotter_qrte.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py index 3885ef966bfa..abe02e95c156 100644 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py @@ -118,12 +118,12 @@ def quantum_instance(self, quantum_instance: Optional[Union[QuantumInstance, Bac self._quantum_instance = quantum_instance @property - def expectation(self) -> ExpectationBase: + def expectation(self) -> Optional[ExpectationBase]: """Returns an expectation used in the algorithm.""" return self._expectation @expectation.setter - def expectation(self, expectation: ExpectationBase) -> None: + def expectation(self, expectation: Optional[ExpectationBase]) -> None: """ Sets an expectation. Args: