Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Numerical Integration methods for real and imaginary time evolution. #8399

Merged
merged 56 commits into from
Dec 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
871f29e
All Changes so far
MarcDrudis Jun 13, 2022
0fad582
Revert "All Changes so far"
MarcDrudis Jun 13, 2022
99378d0
Merge branch 'Qiskit:main' into main
MarcDrudis Jun 29, 2022
2d228e9
Merge branch 'Qiskit:main' into QuantumTimeEvolution
MarcDrudis Jul 14, 2022
3f8312c
Quantum Time Evolution
MarcDrudis Jul 18, 2022
3682ef0
operators and frob norm
MarcDrudis Jul 18, 2022
0a51b89
fro norm
MarcDrudis Jul 18, 2022
c8b757b
Changes
MarcDrudis Jul 22, 2022
3f03011
changes
MarcDrudis Jul 22, 2022
ee99c91
Added history
MarcDrudis Jul 25, 2022
eae37b2
Merge branch 'Qiskit:main' into QuantumTimeEvolution
MarcDrudis Jul 26, 2022
1d8c4f8
Removed files and fixed lint
MarcDrudis Jul 26, 2022
8474ee7
Merge branch 'QuantumTimeEvolution' of https://github.com/MarcDrudis/…
MarcDrudis Jul 26, 2022
c9109d4
Changed name
MarcDrudis Jul 26, 2022
dbe39d9
fixed accuracy associated to bicg
MarcDrudis Jul 26, 2022
580bdc6
Changed imaginary
MarcDrudis Jul 27, 2022
5be480f
Fixed output for observables
MarcDrudis Jul 28, 2022
abc153e
version
MarcDrudis Jul 29, 2022
25c4198
changes
MarcDrudis Aug 4, 2022
85b8a9d
Comments Part 1
MarcDrudis Aug 22, 2022
0009410
Merge branch 'main' into QuantumTimeEvolution
MarcDrudis Aug 22, 2022
8bea18b
make black
MarcDrudis Aug 23, 2022
621172f
Merge branch 'QuantumTimeEvolution' of https://github.com/MarcDrudis/…
MarcDrudis Aug 23, 2022
41f0663
Changed output observalbes
MarcDrudis Aug 24, 2022
85d25a6
Merge branch 'main' into QuantumTimeEvolution
MarcDrudis Aug 24, 2022
ce3ea25
Changed to taylor expansion
MarcDrudis Aug 25, 2022
d2139ac
black
MarcDrudis Aug 26, 2022
94708e5
Documentation
MarcDrudis Aug 29, 2022
d841f88
black
MarcDrudis Aug 29, 2022
025cac7
Some comments
MarcDrudis Sep 14, 2022
ff36e3a
redesign
MarcDrudis Sep 25, 2022
1a7dbce
Merge branch 'main' into QuantumTimeEvolution
MarcDrudis Sep 25, 2022
4a4531d
changes
MarcDrudis Sep 26, 2022
222e50a
restore evolvers
MarcDrudis Sep 26, 2022
39dd42d
unittest redoing path
MarcDrudis Sep 26, 2022
d0df86d
make black
MarcDrudis Sep 26, 2022
5234a7b
Fixed docs?
MarcDrudis Sep 29, 2022
f4ecb8f
Merge branch 'main' into QuantumTimeEvolution
MarcDrudis Sep 29, 2022
b083008
Fixed issue with scipy1.7.3
MarcDrudis Sep 29, 2022
598caa4
Merge branch 'QuantumTimeEvolution' of https://github.com/MarcDrudis/…
MarcDrudis Sep 29, 2022
f48d352
Delete 1.8
MarcDrudis Sep 29, 2022
cda51d1
Review
MarcDrudis Nov 9, 2022
6929de7
Merge branch 'main' into QuantumTimeEvolution
MarcDrudis Nov 10, 2022
950138d
Deleted opflow
MarcDrudis Nov 18, 2022
af80588
times documentation T_ev_result
MarcDrudis Nov 21, 2022
2a24062
Merge branch 'main' into QuantumTimeEvolution
MarcDrudis Nov 21, 2022
e5ab691
Added example to reno
MarcDrudis Nov 21, 2022
0945d9d
Merge branch 'QuantumTimeEvolution' of https://github.com/MarcDrudis/…
MarcDrudis Nov 21, 2022
49fd5d7
Julien review
MarcDrudis Dec 2, 2022
9110168
Update releasenotes/notes/scipy-evolvers-ca92bcb90e90b035.yaml
Cryoris Dec 6, 2022
92bf538
Update releasenotes/notes/scipy-evolvers-ca92bcb90e90b035.yaml
MarcDrudis Dec 12, 2022
d399edc
observable in reno
MarcDrudis Dec 12, 2022
d17adc3
Merge branch 'main' into QuantumTimeEvolution
Cryoris Dec 15, 2022
d53c201
Merge branch 'main' into QuantumTimeEvolution
Cryoris Dec 19, 2022
db83b6f
Merge branch 'main' into QuantumTimeEvolution
Cryoris Dec 20, 2022
849dd9c
Merge branch 'main' into QuantumTimeEvolution
mergify[bot] Dec 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions qiskit/algorithms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@
PVQDResult
TimeEvolutionResult
TimeEvolutionProblem
SciPyImaginaryEvolver
SciPyRealEvolver


Trotterization-based Quantum Real Time Evolution
Expand Down Expand Up @@ -350,6 +352,7 @@
from .evolvers.trotterization import TrotterQRTE

from .time_evolvers.pvqd import PVQD, PVQDResult
from .time_evolvers.classical_methods import SciPyRealEvolver, SciPyImaginaryEvolver

__all__ = [
"AlgorithmJob",
Expand Down Expand Up @@ -404,6 +407,8 @@
"PhaseEstimationResult",
"PVQD",
"PVQDResult",
"SciPyRealEvolver",
"SciPyImaginaryEvolver",
"IterativePhaseEstimation",
"AlgorithmError",
"eval_observables",
Expand Down
10 changes: 10 additions & 0 deletions qiskit/algorithms/time_evolvers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

"""Quantum Time Evolution package."""

from .time_evolution_result import TimeEvolutionResult
from .time_evolution_problem import TimeEvolutionProblem

__all__ = [
"TimeEvolutionResult",
"TimeEvolutionProblem",
]
18 changes: 18 additions & 0 deletions qiskit/algorithms/time_evolvers/classical_methods/__init__.py
Original file line number Diff line number Diff line change
@@ -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.

"""Classical Methods for Quantum Time Evolution package."""

from .scipy_real_evolver import SciPyRealEvolver
from .scipy_imaginary_evolver import SciPyImaginaryEvolver

__all__ = ["SciPyRealEvolver", "SciPyImaginaryEvolver"]
219 changes: 219 additions & 0 deletions qiskit/algorithms/time_evolvers/classical_methods/evolve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# 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.
"""Auxiliary functions for SciPy Time Evolvers"""
from __future__ import annotations
import logging
from scipy.sparse import csr_matrix
from scipy.sparse.linalg import expm_multiply
import numpy as np

from qiskit.quantum_info.states import Statevector
from qiskit.quantum_info.operators.base_operator import BaseOperator

from qiskit import QuantumCircuit
from qiskit.opflow import PauliSumOp
from ..time_evolution_problem import TimeEvolutionProblem
from ..time_evolution_result import TimeEvolutionResult
from ...exceptions import AlgorithmError

from ...list_or_dict import ListOrDict

logger = logging.getLogger(__name__)


def _create_observable_output(
ops_ev_mean: np.ndarray,
evolution_problem: TimeEvolutionProblem,
) -> tuple[ListOrDict[tuple[np.ndarray, np.ndarray]], np.ndarray]:
"""Creates the right output format for the evaluated auxiliary operators.
Args:
ops_ev_mean: Array containing the expectation value of each observable at each timestep.
evolution_problem: Time Evolution Problem to create the output of.

Returns:
An output with the observables mean value at the appropriate times depending on whether
the auxiliary operators in the time evolution problem are a `list` or a `dict`.

"""

aux_ops = evolution_problem.aux_operators

time_array = np.linspace(0, evolution_problem.time, ops_ev_mean.shape[-1])
zero_array = np.zeros(ops_ev_mean.shape[-1]) # std=0 since it is an exact method

operators_number = 0 if aux_ops is None else len(aux_ops)

observable_evolution = [(ops_ev_mean[i], zero_array) for i in range(operators_number)]

if isinstance(aux_ops, dict):
observable_evolution = dict(zip(aux_ops.keys(), observable_evolution))

return observable_evolution, time_array


def _create_obs_final(
ops_ev_mean: np.ndarray,
evolution_problem: TimeEvolutionProblem,
) -> ListOrDict[tuple[complex, complex]]:
"""Creates the right output format for the final value of the auxiliary operators.

Args:
ops_ev_mean: Array containing the expectation value of each observable at the final timestep.
evolution_problem: Evolution problem to create the output of.

Returns:
An output with the observables mean value at the appropriate times depending on whether
the auxiliary operators in the evolution problem are a `list` or a `dict`.

"""

aux_ops = evolution_problem.aux_operators
aux_ops_evaluated = [(op_ev, 0) for op_ev in ops_ev_mean]
if isinstance(aux_ops, dict):
aux_ops_evaluated = dict(zip(aux_ops.keys(), aux_ops_evaluated))
return aux_ops_evaluated


def _evaluate_aux_ops(
aux_ops: list[csr_matrix],
state: np.ndarray,
) -> tuple[np.ndarray, np.ndarray]:
"""Evaluates the aux operators if they are provided and stores their value.

Returns:
Tuple of the mean and standard deviation of the aux operators for a given state.
"""
op_means = np.array([np.real(state.conjugate().dot(op.dot(state))) for op in aux_ops])
return op_means


def _operator_to_matrix(operator: BaseOperator | PauliSumOp):

if isinstance(operator, PauliSumOp):
op_matrix = operator.to_spmatrix()
else:
try:
op_matrix = operator.to_matrix(sparse=True)
except TypeError:
logger.debug(
"WARNING: operator of type `%s` does not support sparse matrices. "
"Trying dense computation",
type(operator),
)
try:
op_matrix = operator.to_matrix()
except AttributeError as ex:
raise AlgorithmError(f"Unsupported operator type `{type(operator)}`.") from ex
return op_matrix


def _build_scipy_operators(
evolution_problem: TimeEvolutionProblem, num_timesteps: int, real_time: bool
) -> tuple[np.ndarray, list[csr_matrix], csr_matrix]:
"""Returns the matrices and parameters needed for time evolution in the appropriate format.

Args:
evolution_problem: The definition of the evolution problem.
num_timesteps: Number of timesteps to be performed.
real_time: If `True`, returned operators will correspond to real time evolution,
Else, they will correspond to imaginary time evolution.

Returns:
A tuple with the initial state, the list of operators to evaluate and the operator to be
exponentiated to perform one timestep.

Raises:
ValueError: If the Hamiltonian can not be converted into a sparse matrix or dense matrix.
"""
# Convert the initial state and Hamiltonian into sparse matrices.
if isinstance(evolution_problem.initial_state, QuantumCircuit):
state = Statevector(evolution_problem.initial_state).data
else:
state = evolution_problem.initial_state.data

hamiltonian = _operator_to_matrix(operator=evolution_problem.hamiltonian)

if isinstance(evolution_problem.aux_operators, list):
aux_ops = [
_operator_to_matrix(operator=aux_op) for aux_op in evolution_problem.aux_operators
]
elif isinstance(evolution_problem.aux_operators, dict):
aux_ops = [
_operator_to_matrix(operator=aux_op)
for aux_op in evolution_problem.aux_operators.values()
]
else:
aux_ops = []
timestep = evolution_problem.time / num_timesteps
step_operator = -((1.0j) ** real_time) * timestep * hamiltonian
return state, aux_ops, step_operator


def _evolve(
evolution_problem: TimeEvolutionProblem, num_timesteps: int, real_time: bool
) -> TimeEvolutionResult:
r"""Performs either real or imaginary time evolution :math:`\exp(-i t H)|\Psi\rangle`.

Args:
evolution_problem: The definition of the evolution problem.
num_timesteps: Number of timesteps to be performed.
real_time: If `True`, returned operators will correspond to real time evolution,
Else, they will correspond to imaginary time evolution.

Returns:
Evolution result which includes an evolved quantum state.

Raises:
ValueError: If the Hamiltonian is time dependent.
ValueError: If the initial state is `None`.

"""
if num_timesteps <= 0:
raise ValueError("Variable `num_timesteps` needs to be a positive integer.")

if evolution_problem.t_param is not None:
raise ValueError("Time dependent Hamiltonians are not supported.")

if evolution_problem.initial_state is None:
raise ValueError("Initial state is `None`")

state, aux_ops, step_operator = _build_scipy_operators(
evolution_problem=evolution_problem, num_timesteps=num_timesteps, real_time=real_time
)

# Create empty arrays to store the time evolution of the aux operators.
number_operators = (
0 if evolution_problem.aux_operators is None else len(evolution_problem.aux_operators)
)
ops_ev_mean = np.empty(shape=(number_operators, num_timesteps + 1), dtype=complex)

renormalize = (
(lambda state: state) if real_time else (lambda state: state / np.linalg.norm(state))
)

# Perform the time evolution and stores the value of the operators at each timestep.
for ts in range(num_timesteps):
ops_ev_mean[:, ts] = _evaluate_aux_ops(aux_ops, state)
state = expm_multiply(A=step_operator, B=state)
state = renormalize(state)

ops_ev_mean[:, num_timesteps] = _evaluate_aux_ops(aux_ops, state)

observable_history, times = _create_observable_output(ops_ev_mean, evolution_problem)
aux_ops_evaluated = _create_obs_final(ops_ev_mean[:, -1], evolution_problem)

return TimeEvolutionResult(
evolved_state=Statevector(state),
aux_ops_evaluated=aux_ops_evaluated,
observables=observable_history,
times=times,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 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.

"""Classical Quantum Imaginary Time Evolution."""

from ..time_evolution_problem import TimeEvolutionProblem
from ..time_evolution_result import TimeEvolutionResult
from ..imaginary_time_evolver import ImaginaryTimeEvolver
from .evolve import _evolve


class SciPyImaginaryEvolver(ImaginaryTimeEvolver):
r"""Classical Evolver for imaginary time evolution.

Evolves an initial state :math:`|\Psi\rangle` for an imaginary time :math:`\tau = it`
under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``.
Note that the precision of the evolver does not depend on the number of
timesteps taken.
"""

def __init__(self, num_timesteps: int):
r"""
Args:
num_timesteps: The number of timesteps in the simulation.
Raises:
ValueError: If `num_timesteps` is not a positive integer.
"""
self.num_timesteps = num_timesteps

def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult:
r"""Perform imaginary time evolution :math:`\exp(-\tau H)|\Psi\rangle`.

Evolves an initial state :math:`|\Psi\rangle` for an imaginary time :math:`\tau`
under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``.

Args:
evolution_problem: The definition of the evolution problem.

Returns:
Evolution result which includes an evolved quantum state.
"""
return _evolve(evolution_problem, self.num_timesteps, real_time=False)
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# 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.

"""Classical Quantum Real Time Evolution."""
from .evolve import _evolve
from ..time_evolution_problem import TimeEvolutionProblem
from ..time_evolution_result import TimeEvolutionResult
from ..real_time_evolver import RealTimeEvolver


class SciPyRealEvolver(RealTimeEvolver):
r"""Classical Evolver for real time evolution.

Evolves an initial state :math:`|\Psi\rangle` for a time :math:`t`
under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``.
Note that the precision of the evolver does not depend on the number of
timesteps taken.
"""

def __init__(self, num_timesteps: int):
"""
Args:
num_timesteps: The number of timesteps in the simulation.
Raises:
ValueError: If `steps` is not a positive integer.
"""
self.num_timesteps = num_timesteps

def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult:
r"""Perform real time evolution :math:`\exp(-i t H)|\Psi\rangle`.

Evolves an initial state :math:`|\Psi\rangle` for a time :math:`t`
under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``.

Args:
evolution_problem: The definition of the evolution problem.

Returns:
Evolution result which includes an evolved quantum state.
"""
return _evolve(evolution_problem, self.num_timesteps, real_time=True)
2 changes: 1 addition & 1 deletion qiskit/algorithms/time_evolvers/real_time_evolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult
r"""Perform real time evolution :math:`\exp(-i t H)|\Psi\rangle`.

Evolves an initial state :math:`|\Psi\rangle` for a time :math:`t`
under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``.
under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``.

Args:
evolution_problem: The definition of the evolution problem.
Expand Down
Loading