mirror of https://github.com/Qiskit/qiskit.git
Trotterization-based Quantum Real Time Evolution (#7411)
* Introduced time evolution classes and interfaces. * Migrated trotterization as trotterization builder. * Implemented enum for trotterization mode; fixed imports. * Implemented trotter_qrte.py with unit tests. * trotter_qrte.py repetitions added. * Code refactoring. * Code refactoring. * Gradient object introduced in a signature. * Draft of trotter_qrte.py gradient with unit tests. * trotter_qrte.py improvements; unit tests extended. * trotter_qrte.py improvements; unit tests extended. * Trotter test fix. * Fixed float and complex handling. * qrte with product formula * Removed irrelevant files. * Removed irrelevant files. * Extended docs. * Removed outdated unit tests. * Removed outdated files. * Code refactoring, docs extended. * Code refactoring. * Updated unit tests. * Code refactoring, extended docs and typehints. * Reno added. * Extended checks and unit tests. * Code refactoring. * Code refactoring. * fix problem with test * Docs extended. * Added support for PauliOp, code refactoring. * Implemented general Quantum Time Evolution Framework interfaces. * Updated docs. * Reno added. * Improved reno. * Code refactoring. * Update qiskit/algorithms/time_evolution/evolution_base.py Co-authored-by: Julien Gacon <gaconju@gmail.com> * Code refactoring. * Introduced evolution problem classes. * Code refactoring. * Apply suggestions from code review Co-authored-by: Julien Gacon <gaconju@gmail.com> * Added unit tests. * Lint fixed. * Code refactoring * Code refactoring * Code refactoring * Code refactoring * Code refactoring * Code refactoring * Code review changes. * Removed gradient code for now. * Code review refactoring. * Removed gradient code for now. * Evolving observable removed. Evaluating observables added. * Code refactoring. * Improved naming. * Improved folder structure; filled evolvers init file. * Added Evolvers to algorithms init file. * Fixed missing imports. * Code refactoring * Fixed cyclic imports. * Extracted ListOrDict. * Code refactoring. * Code refactoring. * Fixed release note. * Fixed inheritance order. * Updated code to the latest time evolution interface. * Code refactoring. * Code refactoring. * Code refactoring. * Fixed cyclic imports. * Fixed pylint. * Name fix. * Import fix. * Lint fix. * Lint fix. * Code refactoring. * Extracted and refactored aux_ops_evaluator.py * Code refactoring * Implemented unit test. * Extended unit test. * Date fixed. * Reno added. * Switched to bound ansatz. * Fix reno. * Added docs. * Refactored unit test. * Lint fixed. * Code review edits. * Added unit test cases for dicts. * Fixed reno reference. * Improved unit test. * Added quantum instance support. * Added support for aux_ops and unit test. * Fixed input object to eval_observables. * Fixed quantum_state types and conversion. * Fixed quantum_state types and conversion. * Fixed types and docs. * Code refactoring. * Lint fix. * Code refactoring. * Reno update. * Code refactoring. * Implemented some CR feedback. * evolution_problem.py fix * Removed trotter_ops_validator.py for now, extended unit tests, code refactoring. * Updated names of the algorithm. * Added support for QuantumCircuit input. Code refactoring. * Improved error handling in evolution_problem.py and unit tests added. * Code refactoring. * Cyclic import fix * Refactored evolution_problem.py * Refactored test_trotter_qrte.py * Removed global tolerance. * CI fix. * Code review fixes. * Code refactoring. * Prepared qasm unit test. * Removing state eval. * Refactored and updated tests. * Added qasm test for aux ops. * Added param binding test. * Added missing setters. * Updated unit tests. * Improved folder structure. * Updated interfaces. * Fix lint. * Removed legacy (soon) BaseBackend. * Updated copyright years. * Added example to reno. * Fixed reno. * Removed BaseOperator. * Code refactoring. * Imports fix. * Delayed parameters checks. * Update qiskit/algorithms/evolvers/evolution_problem.py Co-authored-by: Julien Gacon <gaconju@gmail.com> * Code refactoring. * Update qiskit/algorithms/evolvers/evolution_problem.py Co-authored-by: Julien Gacon <gaconju@gmail.com> * Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon <gaconju@gmail.com> * Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon <gaconju@gmail.com> * Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon <gaconju@gmail.com> * Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon <gaconju@gmail.com> * Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon <gaconju@gmail.com> * Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon <gaconju@gmail.com> * Update qiskit/algorithms/evolvers/trotterization/trotter_qrte.py Co-authored-by: Julien Gacon <gaconju@gmail.com> * Expectation might be None Co-authored-by: acv@zurich.ibm.com <acv@zurich.ibm.com> Co-authored-by: Julien Gacon <gaconju@gmail.com>
This commit is contained in:
parent
7e83de6d80
commit
18b5a048ea
|
@ -106,6 +106,7 @@ used to train Quantum Boltzmann Machine Neural Networks for example.
|
|||
|
||||
RealEvolver
|
||||
ImaginaryEvolver
|
||||
TrotterQRTE
|
||||
EvolutionResult
|
||||
EvolutionProblem
|
||||
|
||||
|
@ -205,8 +206,8 @@ Utility methods used by algorithms.
|
|||
|
||||
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 .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 (
|
||||
|
@ -243,6 +244,7 @@ from .phase_estimators import (
|
|||
)
|
||||
from .exceptions import AlgorithmError
|
||||
from .aux_ops_evaluator import eval_observables
|
||||
from .evolvers.trotterization import TrotterQRTE
|
||||
|
||||
__all__ = [
|
||||
"AlgorithmResult",
|
||||
|
@ -266,6 +268,7 @@ __all__ = [
|
|||
"NumPyEigensolver",
|
||||
"RealEvolver",
|
||||
"ImaginaryEvolver",
|
||||
"TrotterQRTE",
|
||||
"EvolutionResult",
|
||||
"EvolutionProblem",
|
||||
"LinearSolverResult",
|
||||
|
|
|
@ -23,9 +23,8 @@ from ..list_or_dict import ListOrDict
|
|||
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__(
|
||||
|
@ -34,8 +33,9 @@ class EvolutionProblem:
|
|||
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,
|
||||
hamiltonian_value_dict: Optional[Dict[Parameter, complex]] = None,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
|
@ -44,15 +44,64 @@ class EvolutionProblem:
|
|||
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
|
||||
dictionary maps all these parameters to values.
|
||||
|
||||
Raises:
|
||||
ValueError: If non-positive time of evolution is provided.
|
||||
"""
|
||||
|
||||
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
|
||||
self.t_param = t_param
|
||||
self.hamiltonian_value_dict = hamiltonian_value_dict
|
||||
self.truncation_threshold = truncation_threshold
|
||||
|
||||
@property
|
||||
def time(self) -> float:
|
||||
"""Returns time."""
|
||||
return self._time
|
||||
|
||||
@time.setter
|
||||
def time(self, time: float) -> None:
|
||||
"""
|
||||
Sets time and validates it.
|
||||
|
||||
Raises:
|
||||
ValueError: If time is not positive.
|
||||
"""
|
||||
if time <= 0:
|
||||
raise ValueError(f"Evolution time must be > 0 but was {time}.")
|
||||
self._time = time
|
||||
|
||||
def validate_params(self) -> None:
|
||||
"""
|
||||
Checks if all parameters present in the Hamiltonian are also present in the dictionary
|
||||
that maps them to values.
|
||||
|
||||
Raises:
|
||||
ValueError: If Hamiltonian parameters cannot be bound with data provided.
|
||||
"""
|
||||
if isinstance(self.hamiltonian, OperatorBase):
|
||||
t_param_set = set()
|
||||
if self.t_param is not None:
|
||||
t_param_set.add(self.t_param)
|
||||
hamiltonian_dict_param_set = set()
|
||||
if self.hamiltonian_value_dict is not None:
|
||||
hamiltonian_dict_param_set = hamiltonian_dict_param_set.union(
|
||||
set(self.hamiltonian_value_dict.keys())
|
||||
)
|
||||
params_set = t_param_set.union(hamiltonian_dict_param_set)
|
||||
hamiltonian_param_set = set(self.hamiltonian.parameters)
|
||||
|
||||
if hamiltonian_param_set != params_set:
|
||||
raise ValueError(
|
||||
f"Provided parameters {params_set} do not match Hamiltonian parameters "
|
||||
f"{hamiltonian_param_set}."
|
||||
)
|
||||
|
|
|
@ -16,7 +16,7 @@ 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.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,
|
||||
):
|
||||
"""
|
||||
|
|
|
@ -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):
|
|
@ -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):
|
|
@ -9,3 +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.
|
||||
"""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.evolvers.trotterization.trotter_qrte import (
|
||||
TrotterQRTE,
|
||||
)
|
||||
|
||||
__all__ = ["TrotterQRTE"]
|
|
@ -0,0 +1,245 @@
|
|||
# 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 typing import Union, Optional
|
||||
|
||||
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_evolver import RealEvolver
|
||||
from qiskit.opflow import (
|
||||
SummedOp,
|
||||
PauliOp,
|
||||
CircuitOp,
|
||||
ExpectationBase,
|
||||
CircuitSampler,
|
||||
PauliSumOp,
|
||||
StateFn,
|
||||
OperatorBase,
|
||||
)
|
||||
from qiskit.circuit.library import PauliEvolutionGate
|
||||
from qiskit.providers import Backend
|
||||
from qiskit.synthesis import ProductFormula, LieTrotter
|
||||
from qiskit.utils import QuantumInstance
|
||||
|
||||
|
||||
class TrotterQRTE(RealEvolver):
|
||||
"""Quantum Real Time Evolution using Trotterization.
|
||||
Type of Trotterization is defined by a ProductFormula provided.
|
||||
|
||||
Examples:
|
||||
|
||||
.. jupyter-execute::
|
||||
|
||||
from qiskit.opflow import X, Z, Zero
|
||||
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
|
||||
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: Optional[ProductFormula] = None,
|
||||
expectation: Optional[ExpectationBase] = None,
|
||||
quantum_instance: Optional[Union[QuantumInstance, Backend]] = 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.
|
||||
expectation: An instance of ExpectationBase which defines a method for calculating
|
||||
expectation values of EvolutionProblem.aux_operators.
|
||||
quantum_instance: A quantum instance used for calculating expectation values of
|
||||
EvolutionProblem.aux_operators.
|
||||
"""
|
||||
if product_formula is None:
|
||||
product_formula = LieTrotter()
|
||||
self._product_formula = product_formula
|
||||
self._quantum_instance = None
|
||||
self._circuit_sampler = None
|
||||
if quantum_instance is not None:
|
||||
self.quantum_instance = quantum_instance
|
||||
self._expectation = expectation
|
||||
|
||||
@property
|
||||
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: ProductFormula) -> 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) -> Optional[QuantumInstance]:
|
||||
"""Returns a quantum instance used in the algorithm."""
|
||||
return self._quantum_instance
|
||||
|
||||
@quantum_instance.setter
|
||||
def quantum_instance(self, quantum_instance: Optional[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, Backend):
|
||||
quantum_instance = QuantumInstance(quantum_instance)
|
||||
|
||||
self._circuit_sampler = None
|
||||
if quantum_instance is not None:
|
||||
self._circuit_sampler = CircuitSampler(quantum_instance)
|
||||
|
||||
self._quantum_instance = quantum_instance
|
||||
|
||||
@property
|
||||
def expectation(self) -> Optional[ExpectationBase]:
|
||||
"""Returns an expectation used in the algorithm."""
|
||||
return self._expectation
|
||||
|
||||
@expectation.setter
|
||||
def expectation(self, expectation: Optional[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:
|
||||
"""
|
||||
Whether computing the expectation value of auxiliary operators is supported.
|
||||
|
||||
Returns:
|
||||
True if ``aux_operators`` expectations in the EvolutionProblem can be evaluated, False
|
||||
otherwise.
|
||||
"""
|
||||
return True
|
||||
|
||||
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. 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.
|
||||
|
||||
Args:
|
||||
evolution_problem: Instance defining evolution problem. For the included Hamiltonian,
|
||||
``PauliOp``, ``SummedOp`` 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 a backend.
|
||||
|
||||
Raises:
|
||||
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.
|
||||
"""
|
||||
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 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 were provided for evaluations but no ``expectation`` or "
|
||||
"``quantum_instance`` was provided."
|
||||
)
|
||||
hamiltonian = evolution_problem.hamiltonian
|
||||
if not isinstance(hamiltonian, (PauliOp, PauliSumOp, SummedOp)):
|
||||
raise ValueError(
|
||||
"TrotterQRTE only accepts PauliOp | "
|
||||
f"PauliSumOp | SummedOp, {type(hamiltonian)} provided."
|
||||
)
|
||||
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)
|
||||
# the evolution gate
|
||||
evolution_gate = CircuitOp(
|
||||
PauliEvolutionGate(hamiltonian, evolution_problem.time, synthesis=self._product_formula)
|
||||
)
|
||||
|
||||
if evolution_problem.initial_state is not None:
|
||||
initial_state = evolution_problem.initial_state
|
||||
if isinstance(initial_state, QuantumCircuit):
|
||||
initial_state = StateFn(initial_state)
|
||||
evolved_state = evolution_gate @ initial_state
|
||||
|
||||
else:
|
||||
raise ValueError("``initial_state`` must be provided in the EvolutionProblem.")
|
||||
|
||||
evaluated_aux_ops = None
|
||||
if evolution_problem.aux_operators is not None:
|
||||
evaluated_aux_ops = eval_observables(
|
||||
self._quantum_instance,
|
||||
evolved_state.primitive,
|
||||
evolution_problem.aux_operators,
|
||||
self._expectation,
|
||||
evolution_problem.truncation_threshold,
|
||||
)
|
||||
|
||||
return EvolutionResult(evolved_state, evaluated_aux_ops)
|
||||
|
||||
@staticmethod
|
||||
def _summed_op_to_pauli_sum_op(
|
||||
hamiltonian: SummedOp,
|
||||
) -> Union[PauliSumOp, PauliOp]:
|
||||
"""
|
||||
Tries binding parameters in a Hamiltonian.
|
||||
|
||||
Args:
|
||||
hamiltonian: The Hamiltonian that defines an evolution.
|
||||
|
||||
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):
|
||||
raise ValueError(
|
||||
"Content of the Hamiltonian not of type PauliOp. The "
|
||||
f"following type detected: {type(op)}."
|
||||
)
|
||||
op_list.append(op)
|
||||
return sum(op_list)
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
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.
|
||||
|
||||
.. 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
|
||||
evolution_problem = EvolutionProblem(operator, time, initial_state)
|
||||
|
||||
trotter_qrte = TrotterQRTE()
|
||||
evolution_result = trotter_qrte.evolve(evolution_problem)
|
||||
evolved_state_circuit = evolution_result.evolved_state
|
|
@ -12,13 +12,16 @@
|
|||
|
||||
"""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 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."""
|
||||
|
||||
|
@ -54,7 +57,12 @@ class TestEvolutionProblem(QiskitAlgorithmsTestCase):
|
|||
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
|
||||
|
@ -71,6 +79,44 @@ class TestEvolutionProblem(QiskitAlgorithmsTestCase):
|
|||
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])
|
||||
@unpack
|
||||
def test_init_errors(self, hamiltonian, time, initial_state):
|
||||
"""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()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# This code is part of Qiskit.
|
||||
#
|
||||
# (C) Copyright IBM 2021, 2022.
|
||||
# (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
|
|
@ -0,0 +1,244 @@
|
|||
# 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.opflow import QiskitOpflowTestCase
|
||||
from ddt import ddt, data, unpack
|
||||
import numpy as np
|
||||
from numpy.testing import assert_raises
|
||||
|
||||
from qiskit import BasicAer, QuantumCircuit
|
||||
from qiskit.algorithms import EvolutionProblem
|
||||
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 qiskit.opflow import (
|
||||
X,
|
||||
Z,
|
||||
Zero,
|
||||
VectorStateFn,
|
||||
StateFn,
|
||||
I,
|
||||
Y,
|
||||
SummedOp,
|
||||
ExpectationFactory,
|
||||
)
|
||||
from qiskit.synthesis import SuzukiTrotter, QDrift
|
||||
|
||||
|
||||
@ddt
|
||||
class TestTrotterQRTE(QiskitOpflowTestCase):
|
||||
"""TrotterQRTE tests."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.seed = 50
|
||||
algorithm_globals.random_seed = self.seed
|
||||
backend_statevector = BasicAer.get_backend("statevector_simulator")
|
||||
backend_qasm = BasicAer.get_backend("qasm_simulator")
|
||||
self.quantum_instance = QuantumInstance(
|
||||
backend=backend_statevector,
|
||||
shots=1,
|
||||
seed_simulator=self.seed,
|
||||
seed_transpiler=self.seed,
|
||||
)
|
||||
self.quantum_instance_qasm = QuantumInstance(
|
||||
backend=backend_qasm,
|
||||
shots=8000,
|
||||
seed_simulator=self.seed,
|
||||
seed_transpiler=self.seed,
|
||||
)
|
||||
self.backends_dict = {
|
||||
"qi_sv": self.quantum_instance,
|
||||
"qi_qasm": self.quantum_instance_qasm,
|
||||
"b_sv": backend_statevector,
|
||||
"None": None,
|
||||
}
|
||||
|
||||
self.backends_names = ["qi_qasm", "b_sv", "None", "qi_sv"]
|
||||
self.backends_names_not_none = ["qi_sv", "b_sv", "qi_qasm"]
|
||||
|
||||
@data(
|
||||
(
|
||||
None,
|
||||
VectorStateFn(
|
||||
Statevector([0.29192658 - 0.45464871j, 0.70807342 - 0.45464871j], dims=(2,))
|
||||
),
|
||||
),
|
||||
(
|
||||
SuzukiTrotter(),
|
||||
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):
|
||||
"""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)
|
||||
|
||||
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]
|
||||
|
||||
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)]
|
||||
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)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
@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)
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
@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)
|
||||
|
||||
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."""
|
||||
# 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))
|
||||
)
|
||||
trotter_qrte = TrotterQRTE()
|
||||
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(ZGate(), [0]),
|
||||
VectorStateFn(
|
||||
Statevector([0.23071786 - 0.69436148j, 0.4646314 - 0.49874749j], dims=(2,))
|
||||
),
|
||||
),
|
||||
)
|
||||
@unpack
|
||||
def test_trotter_qrte_qdrift(self, initial_state, expected_state):
|
||||
"""Test for TrotterQRTE with QDrift."""
|
||||
operator = SummedOp([X, Z])
|
||||
time = 1
|
||||
evolution_problem = EvolutionProblem(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_equal(evolution_result.evolved_state.eval(), expected_state)
|
||||
|
||||
@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."""
|
||||
operator = X * Parameter("t") + Z
|
||||
initial_state = Zero
|
||||
time = 1
|
||||
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__":
|
||||
unittest.main()
|
Loading…
Reference in New Issue