-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Implemented TrotterQRTE algorithm with primitives. #8706
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 76 commits
e3233b7
d71f50b
7a7c82b
54afe25
d2b67d9
c1276d2
e06c6c2
5a9bebe
356deae
14ba35d
4cf6f61
ebedff2
062162e
a4a51cc
39c9735
99adde7
9e0c0d0
221ee29
6462bb0
604944c
d287c2f
23821e0
80a2e2b
a2960b2
9b5f50a
4c40e00
f9d2fce
5f4896a
183563b
90bed9e
107dc2b
2bcf07b
4adfd1b
387ce78
f5c3eaf
65a2cab
a57b073
553714c
a7e2f60
34c2deb
5c87255
d03bddf
a656db0
fc842a9
92d6515
9ba37ed
7680019
58a76d6
b2fe7c8
85263b8
5cd8e19
e18ba90
20940d5
1dce473
24aa2bf
00d9d11
4cceb01
661b631
44b7959
12d9a68
d3c601e
773b124
6a968f3
2610736
d8154ed
3d706a7
d248e44
a7e615f
fad2caa
b27ed9b
68069f5
46512f1
4307172
1127170
b0471c3
45b9b65
8563b2d
0f43b02
28f2645
7de440d
e7032ff
d09d21e
f78ad60
8f7e739
3f881e9
0fe14e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # 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. | ||
| """This package contains Trotterization-based Quantum Real Time Evolution algorithm. | ||
| 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.time_evolvers.trotterization.trotter_qrte import TrotterQRTE | ||
|
|
||
| __all__ = ["TrotterQRTE"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| # 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. | ||
|
|
||
| """An algorithm to implement a Trotterization real time-evolution.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import warnings | ||
|
|
||
| from qiskit.algorithms.observables_evaluator import estimate_observables | ||
|
dlasecki marked this conversation as resolved.
Outdated
|
||
|
|
||
| from qiskit import QuantumCircuit | ||
| from qiskit.algorithms.time_evolvers.time_evolution_problem import TimeEvolutionProblem | ||
| from qiskit.algorithms.time_evolvers.time_evolution_result import TimeEvolutionResult | ||
| from qiskit.algorithms.time_evolvers.real_time_evolver import RealTimeEvolver | ||
| from qiskit.opflow import PauliSumOp | ||
| from qiskit.circuit.library import PauliEvolutionGate | ||
| from qiskit.primitives import BaseEstimator | ||
| from qiskit.quantum_info import Pauli | ||
| from qiskit.synthesis import ProductFormula, LieTrotter | ||
|
|
||
|
|
||
| class TrotterQRTE(RealTimeEvolver): | ||
| """Quantum Real Time Evolution using Trotterization. | ||
| Type of Trotterization is defined by a ``ProductFormula`` provided. | ||
|
|
||
| Attributes: | ||
| product_formula: A Lie-Trotter-Suzuki product formula. The default is the Lie-Trotter | ||
|
dlasecki marked this conversation as resolved.
Outdated
|
||
| first order product formula with a single repetition. | ||
|
|
||
| Examples: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| from qiskit.opflow import PauliSumOp | ||
| from qiskit.quantum_info import Pauli, SparsePauliOp | ||
| from qiskit import QuantumCircuit | ||
| from qiskit.algorithms.time_evolvers import TimeEvolutionProblem, TrotterQRTE | ||
|
dlasecki marked this conversation as resolved.
Outdated
|
||
| from qiskit.primitives import Estimator | ||
|
|
||
| operator = PauliSumOp(SparsePauliOp([Pauli("X"), Pauli("Z")])) | ||
| initial_state = QuantumCircuit(1) | ||
| time = 1 | ||
| evolution_problem = TimeEvolutionProblem(operator, time, initial_state) | ||
| # LieTrotter with 1 rep | ||
| estimator = Estimator() | ||
| trotter_qrte = TrotterQRTE(estimator=estimator) | ||
| evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| product_formula: ProductFormula | None = None, | ||
| estimator: BaseEstimator | None = None, | ||
| ) -> None: | ||
| """ | ||
| Args: | ||
| product_formula: A Lie-Trotter-Suzuki product formula. The default is the Lie-Trotter | ||
| first order product formula with a single repetition. | ||
| estimator: An estimator primitive used for calculating expectation values of | ||
| ``TimeEvolutionProblem.aux_operators``. | ||
| """ | ||
| if product_formula is None: | ||
| product_formula = LieTrotter() | ||
| self.product_formula = product_formula | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a public instance var, we should document it via Attributes so it's exposed via the API ref. Any reason not to have estimator as a public instance var too and drop the setter/getter (dropping these and going for public instance vars and docs via Attributes is something we have been trying to do recently and in the ongoing refactoring where possible).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned above we have a mix and match here. VQE, PVQD etc seem to have estimator as a public instance var. Since the setter getter here is trivial it would seem a public instance var would be more consistent with the other algos being redone and our desire to move away from having trivial getter/setters. |
||
| self._estimator = estimator | ||
|
|
||
| @property | ||
| def estimator(self) -> BaseEstimator | None: | ||
| """ | ||
| Returns an estimator. | ||
| """ | ||
| return self._estimator | ||
|
|
||
| @estimator.setter | ||
| def estimator(self, estimator: BaseEstimator) -> None: | ||
| """ | ||
| Sets an estimator. | ||
| """ | ||
| self._estimator = estimator | ||
|
|
||
| @classmethod | ||
| def supports_aux_operators(cls) -> bool: | ||
| """ | ||
| Whether computing the expectation value of auxiliary operators is supported. | ||
|
|
||
| Returns: | ||
| ``True`` if ``aux_operators`` expectations in the ``TimeEvolutionProblem`` can be | ||
| evaluated, ``False`` otherwise. | ||
| """ | ||
| return True | ||
|
|
||
| def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult: | ||
| """ | ||
| Evolves a quantum state for a given time using the Trotterization method | ||
| 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 an estimator primitive provided. | ||
|
|
||
| .. note:: | ||
| Time-dependent Hamiltonians are not yet supported. | ||
|
dlasecki marked this conversation as resolved.
Outdated
|
||
|
|
||
| Args: | ||
| evolution_problem: Instance defining evolution problem. For the included Hamiltonian, | ||
| ``Pauli`` or ``PauliSumOp`` are supported by TrotterQRTE. | ||
|
|
||
| Returns: | ||
| Evolution result that includes an evolved state as a quantum circuit and, optionally, | ||
| auxiliary operators evaluated for a resulting state on an estimator primitive. | ||
|
|
||
| Raises: | ||
| ValueError: If ``t_param`` is not set to ``None`` in the ``TimeEvolutionProblem`` | ||
| (feature not currently supported). | ||
| ValueError: If the ``initial_state`` is not provided in the ``TimeEvolutionProblem``. | ||
| ValueError: If an unsupported Hamiltonian type is provided. | ||
| """ | ||
| evolution_problem.validate_params() | ||
| if evolution_problem.t_param is not None: | ||
| raise ValueError( | ||
| "TrotterQRTE does not accept a time dependent Hamiltonian," | ||
| "``t_param`` from the ``TimeEvolutionProblem`` should be set to ``None``." | ||
| ) | ||
|
|
||
| if evolution_problem.aux_operators is not None and self.estimator is None: | ||
| warnings.warn( | ||
| "The time evolution problem contained ``aux_operators`` but no estimator was " | ||
| "provided. The algorithm continues without calculating these quantities. " | ||
| ) | ||
|
dlasecki marked this conversation as resolved.
dlasecki marked this conversation as resolved.
Outdated
|
||
| hamiltonian = evolution_problem.hamiltonian | ||
| if not isinstance(hamiltonian, (Pauli, PauliSumOp)): | ||
| raise ValueError( | ||
| f"TrotterQRTE only accepts Pauli | PauliSumOp, {type(hamiltonian)} provided." | ||
| ) | ||
| # the evolution gate | ||
| evolution_gate = PauliEvolutionGate( | ||
| hamiltonian, evolution_problem.time, synthesis=self.product_formula | ||
| ) | ||
|
|
||
| if evolution_problem.initial_state is not None: | ||
| initial_state = evolution_problem.initial_state | ||
| evolved_state = QuantumCircuit(initial_state.num_qubits) | ||
| evolved_state.append(initial_state, evolved_state.qubits) | ||
| evolved_state.append(evolution_gate, evolved_state.qubits) | ||
|
|
||
| else: | ||
| raise ValueError("``initial_state`` must be provided in the ``TimeEvolutionProblem``.") | ||
|
|
||
| evaluated_aux_ops = None | ||
| if evolution_problem.aux_operators is not None: | ||
| evaluated_aux_ops = estimate_observables( | ||
| self.estimator, | ||
| evolved_state, | ||
| evolution_problem.aux_operators, | ||
| evolution_problem.truncation_threshold, | ||
| ) | ||
|
|
||
| return TimeEvolutionResult(evolved_state, evaluated_aux_ops) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| --- | ||
| features: | ||
| - | | ||
| Added :class:`qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE` with | ||
| :class:`qiskit.primitives.BaseEstimator` as ``init`` parameter. It will soon replace | ||
|
dlasecki marked this conversation as resolved.
Outdated
|
||
| :class:`qiskit.algorithms.evolvers.trotterization.TrotterQRTE`. | ||
|
dlasecki marked this conversation as resolved.
Outdated
|
||
| deprecations: | ||
| - | | ||
| Using :class:`qiskit.algorithms.evolvers.trotterization.TrotterQRTE` will now issue a | ||
|
dlasecki marked this conversation as resolved.
Outdated
|
||
| ``PendingDeprecationWarning``. This method will be deprecated in a future release and | ||
|
dlasecki marked this conversation as resolved.
Outdated
|
||
| subsequently removed after that. This is being replaced by the new | ||
| :class:`qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE` primitive-enabled class. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| # 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. | ||
|
|
||
| """ Test TrotterQRTE. """ | ||
|
|
||
| import unittest | ||
|
|
||
| from test.python.algorithms import QiskitAlgorithmsTestCase | ||
| from ddt import ddt, data, unpack | ||
| import numpy as np | ||
| from numpy.testing import assert_raises | ||
|
|
||
| from qiskit.algorithms.time_evolvers.time_evolution_problem import TimeEvolutionProblem | ||
| from qiskit.algorithms.time_evolvers.trotterization.trotter_qrte import TrotterQRTE | ||
| from qiskit.primitives import Estimator | ||
| from qiskit import QuantumCircuit | ||
| from qiskit.circuit.library import ZGate | ||
| from qiskit.quantum_info import Statevector, Pauli, SparsePauliOp | ||
| from qiskit.utils import algorithm_globals | ||
| from qiskit.circuit import Parameter | ||
| from qiskit.opflow import PauliSumOp | ||
| from qiskit.synthesis import SuzukiTrotter, QDrift | ||
|
|
||
|
|
||
| @ddt | ||
| class TestTrotterQRTE(QiskitAlgorithmsTestCase): | ||
| """TrotterQRTE tests.""" | ||
|
|
||
| def setUp(self): | ||
| super().setUp() | ||
| self.seed = 50 | ||
| algorithm_globals.random_seed = self.seed | ||
|
|
||
| @data( | ||
| ( | ||
| None, | ||
| Statevector([0.29192658 - 0.45464871j, 0.70807342 - 0.45464871j]), | ||
| ), | ||
| ( | ||
| SuzukiTrotter(), | ||
| Statevector([0.29192658 - 0.84147098j, 0.0 - 0.45464871j]), | ||
| ), | ||
| ) | ||
| @unpack | ||
| def test_trotter_qrte_trotter_single_qubit(self, product_formula, expected_state): | ||
| """Test for default TrotterQRTE on a single qubit.""" | ||
| operator = PauliSumOp(SparsePauliOp([Pauli("X"), Pauli("Z")])) | ||
| initial_state = QuantumCircuit(1) | ||
| time = 1 | ||
| evolution_problem = TimeEvolutionProblem(operator, time, initial_state) | ||
|
|
||
| trotter_qrte = TrotterQRTE(product_formula=product_formula) | ||
| evolution_result_state_circuit = trotter_qrte.evolve(evolution_problem).evolved_state | ||
|
|
||
| np.testing.assert_array_almost_equal( | ||
| Statevector.from_instruction(evolution_result_state_circuit).data, expected_state.data | ||
| ) | ||
|
|
||
| def test_trotter_qrte_trotter_single_qubit_aux_ops(self): | ||
| """Test for default TrotterQRTE on a single qubit with auxiliary operators.""" | ||
| operator = PauliSumOp(SparsePauliOp([Pauli("X"), Pauli("Z")])) | ||
| # LieTrotter with 1 rep | ||
| aux_ops = [Pauli("X"), Pauli("Y")] | ||
|
|
||
| initial_state = QuantumCircuit(1) | ||
| time = 3 | ||
| evolution_problem = TimeEvolutionProblem(operator, time, initial_state, aux_ops) | ||
| estimator = Estimator() | ||
|
|
||
| expected_evolved_state = Statevector([0.98008514 + 0.13970775j, 0.01991486 + 0.13970775j]) | ||
|
|
||
| algorithm_globals.random_seed = 0 | ||
| trotter_qrte = TrotterQRTE(estimator=estimator) | ||
| evolution_result = trotter_qrte.evolve(evolution_problem) | ||
|
|
||
| np.testing.assert_array_almost_equal( | ||
| Statevector.from_instruction(evolution_result.evolved_state).data, | ||
| expected_evolved_state.data, | ||
| ) | ||
|
|
||
| aux_ops_result = evolution_result.aux_ops_evaluated | ||
| expected_aux_ops_result = [(0.078073, (0.0, 0.0)), (0.268286, (0.0, 0.0))] | ||
|
|
||
| means = [element[0] for element in aux_ops_result] | ||
| expected_means = [element[0] for element in expected_aux_ops_result] | ||
| np.testing.assert_array_almost_equal(means, expected_means) | ||
|
|
||
| vars_and_shots = [element[1] for element in aux_ops_result] | ||
| expected_vars_and_shots = [element[1] for element in expected_aux_ops_result] | ||
| np.testing.assert_array_equal(vars_and_shots, expected_vars_and_shots) | ||
|
|
||
| @data( | ||
| ( | ||
| PauliSumOp(SparsePauliOp([Pauli("XY"), Pauli("YX")])), | ||
| Statevector([-0.41614684 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.90929743 + 0.0j]), | ||
| ), | ||
| ( | ||
| PauliSumOp(SparsePauliOp([Pauli("ZZ"), Pauli("ZI"), Pauli("IZ")])), | ||
| Statevector([-0.9899925 - 0.14112001j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j]), | ||
| ), | ||
| ( | ||
| Pauli("YY"), | ||
| Statevector([0.54030231 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.84147098j]), | ||
| ), | ||
| ) | ||
| @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 = QuantumCircuit(2) | ||
| evolution_problem = TimeEvolutionProblem(operator, 1, initial_state) | ||
|
|
||
| trotter_qrte = TrotterQRTE() | ||
| evolution_result = trotter_qrte.evolve(evolution_problem) | ||
|
|
||
| np.testing.assert_array_almost_equal( | ||
| Statevector.from_instruction(evolution_result.evolved_state).data, expected_state.data | ||
| ) | ||
|
|
||
| @data( | ||
| (QuantumCircuit(1), Statevector([0.23071786 - 0.69436148j, 0.4646314 - 0.49874749j])), | ||
| ( | ||
| QuantumCircuit(1).compose(ZGate(), [0]), | ||
| Statevector([0.23071786 - 0.69436148j, 0.4646314 - 0.49874749j]), | ||
| ), | ||
| ) | ||
| @unpack | ||
| def test_trotter_qrte_qdrift(self, initial_state, expected_state): | ||
| """Test for TrotterQRTE with QDrift.""" | ||
| operator = PauliSumOp(SparsePauliOp([Pauli("X"), Pauli("Z")])) | ||
| time = 1 | ||
| evolution_problem = TimeEvolutionProblem(operator, time, initial_state) | ||
|
|
||
| algorithm_globals.random_seed = 0 | ||
| trotter_qrte = TrotterQRTE(product_formula=QDrift()) | ||
| evolution_result = trotter_qrte.evolve(evolution_problem) | ||
|
|
||
| np.testing.assert_array_almost_equal( | ||
| Statevector.from_instruction(evolution_result.evolved_state).data, expected_state.data | ||
| ) | ||
|
|
||
| @data((Parameter("t"), {}), (None, {Parameter("x"): 2}), (None, None)) | ||
| @unpack | ||
| def test_trotter_qrte_trotter_errors(self, t_param, param_value_dict): | ||
|
dlasecki marked this conversation as resolved.
Outdated
|
||
| """Test TrotterQRTE with raising errors.""" | ||
| operator = Parameter("t") * PauliSumOp(SparsePauliOp([Pauli("X")])) + PauliSumOp( | ||
| SparsePauliOp([Pauli("Z")]) | ||
| ) | ||
| initial_state = QuantumCircuit(1) | ||
| time = 1 | ||
| algorithm_globals.random_seed = 0 | ||
| trotter_qrte = TrotterQRTE() | ||
| with assert_raises(ValueError): | ||
| evolution_problem = TimeEvolutionProblem( | ||
| operator, | ||
| time, | ||
| initial_state, | ||
| t_param=t_param, | ||
| param_value_map=param_value_dict, | ||
| ) | ||
| _ = trotter_qrte.evolve(evolution_problem) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() | ||
Uh oh!
There was an error while loading. Please reload this page.