Source code for pennylane.ops.functions.evolve
# Copyright 2018-2021 Xanadu Quantum Technologies Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This module contains the qml.evolve function.
"""
from functools import singledispatch
from typing import Optional, overload
from pennylane.operation import Operator
from pennylane.ops import Evolution
from pennylane.pulse import ParametrizedEvolution, ParametrizedHamiltonian
from pennylane.typing import TensorLike
@overload
def evolve(op: ParametrizedHamiltonian, **kwargs) -> ParametrizedEvolution: ...
@overload
def evolve(op: Operator, coeff: TensorLike = 1, num_steps: Optional[int] = None) -> Evolution: ...
[docs]@singledispatch
def evolve(*args, **kwargs): # pylint: disable=unused-argument
r"""This method is dispatched and its functionality depends on the type of the input ``op``.
.. raw:: html
<html>
<h3>Input: Operator</h3>
<hr>
</html>
Returns a new operator that computes the evolution of ``op``.
.. math::
e^{-i x \bm{O}}
Args:
op (.Operator): operator to evolve. This must be passed as a *positional* argument. Passing it as a *keyword* argument will result in an error.
coeff (float): coefficient multiplying the exponentiated operator
num_steps (int): The number of steps used in the decomposition of the exponential operator,
also known as the Trotter number. Defaults to `None`. If this value is `None` and the Suzuki-Trotter
decomposition is needed, an error will be raised.
Returns:
.Evolution: evolution operator
**Examples**
We can use ``qml.evolve`` to compute the evolution of any PennyLane operator:
>>> op = qml.evolve(qml.X(0), coeff=2)
>>> op
Exp(-2j PauliX)
.. raw:: html
<html>
<h3>Input: ParametrizedHamiltonian</h3>
<hr>
</html>
Args:
op (.ParametrizedHamiltonian): Hamiltonian to evolve. This must be passed as a *positional* argument.
Returns:
.ParametrizedEvolution: time evolution :math:`U(t_0, t_1)` of the Hamiltonian
The function takes a :class:`.ParametrizedHamiltonian` and solves the time-dependent Schrodinger equation
.. math:: \frac{\partial}{\partial t} |\psi\rangle = -i H(t) |\psi\rangle
It returns a :class:`~.ParametrizedEvolution`, :math:`U(t_0, t_1)`, which is the solution to the time-dependent
Schrodinger equation for the :class:`~.ParametrizedHamiltonian`, such that
.. math:: |\psi(t_1)\rangle = U(t_0, t_1) |\psi(t_0)\rangle
The :class:`~.ParametrizedEvolution` class uses a numerical ordinary differential equation
solver (`here <https://github.com/google/jax/blob/main/jax/experimental/ode.py>`_).
**Examples**
When evolving a :class:`.ParametrizedHamiltonian`, a :class:`.ParametrizedEvolution`
instance is returned:
.. code-block:: python3
coeffs = [lambda p, t: p * t for _ in range(4)]
ops = [qml.X(i) for i in range(4)]
# ParametrizedHamiltonian
H = qml.dot(coeffs, ops)
# ParametrizedEvolution
ev = qml.evolve(H)
>>> ev
ParametrizedEvolution(wires=[0, 1, 2, 3])
The :class:`.ParametrizedEvolution` is an :class:`~.Operator`, but does not have a defined matrix unless it
is evaluated at set parameters. This is done by calling the :class:`.ParametrizedEvolution`, which has the call
signature ``(p, t)``:
>>> qml.matrix(ev([1., 2., 3., 4.], t=[0, 4]))
Array([[ 0.04930558+0.j , 0. -0.03259093j,
0. +0.1052632j , 0.06957878+0.j ,
0. -0.01482305j, -0.00979751+0.j ,
0.03164552+0.j , 0. -0.0209179j ,
0. +0.33526757j, 0.22161038+0.j ,
...
...
...
0. -0.03259093j, 0.04930566+0.j ]], dtype=complex64)
Additional options regarding how the matrix is calculated can be passed to the :class:`.ParametrizedEvolution`
along with the parameters, as keyword arguments. These options are:
- ``atol (float, optional)``: Absolute error tolerance
- ``rtol (float, optional)``: Relative error tolerance
- ``mxstep (int, optional)``: maximum number of steps to take for each time point
- ``hmax (float, optional)``: maximum step size
If not specified, they will default to predetermined values.
The :class:`~.ParametrizedEvolution` can be implemented in a QNode:
.. code-block:: python
import jax
jax.config.update("jax_enable_x64", True)
dev = qml.device("default.qubit")
@jax.jit
@qml.qnode(dev, interface="jax")
def circuit(params):
qml.evolve(H)(params, t=[0, 10])
return qml.expval(qml.Z(0))
>>> params = [1., 2., 3., 4.]
>>> circuit(params)
Array(0.86231063, dtype=float64)
>>> jax.grad(circuit)(params)
[Array(50.391273, dtype=float64),
Array(-9.42415807e-05, dtype=float64),
Array(-0.0001049, dtype=float64),
Array(-0.00010601, dtype=float64)]
.. note::
In the example above, the decorator ``@jax.jit`` is used to compile this execution just-in-time. This means
the first execution will typically take a little longer with the benefit that all following executions
will be significantly faster, see the jax docs on jitting. JIT-compiling is optional, and one can remove
the decorator when only single executions are of interest.
"""
# pylint: disable=missing-docstring
@evolve.register
def parametrized_evolution(op: ParametrizedHamiltonian, **kwargs):
return ParametrizedEvolution(H=op, **kwargs)
# pylint: disable=missing-docstring
@evolve.register
def evolution(op: Operator, coeff: float = 1, num_steps: int = None):
return Evolution(op, coeff, num_steps)
_modules/pennylane/ops/functions/evolve
Download Python script
Download Notebook
View on GitHub