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

qml.QDrift template #4671

Merged
merged 39 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
89c5e12
QDrift
KetpuntoG Oct 12, 2023
e533cef
Update qdrift.py
KetpuntoG Oct 12, 2023
8c115e3
Update qdrift.py
KetpuntoG Oct 12, 2023
fd46345
Update qdrift.py
KetpuntoG Oct 12, 2023
5ac1d16
sign typo
KetpuntoG Oct 13, 2023
49c747d
black
KetpuntoG Oct 13, 2023
96d3c7a
Update qdrift.py
KetpuntoG Oct 13, 2023
83d1a3c
Update qdrift.py
KetpuntoG Oct 13, 2023
1447038
Update qdrift.py
KetpuntoG Oct 13, 2023
2701987
add figure
soranjh Oct 15, 2023
214fdff
add figure
soranjh Oct 15, 2023
fcf1d9e
add figure
soranjh Oct 15, 2023
9880bd4
use random from math
soranjh Oct 15, 2023
ac8fda6
modify docstring
soranjh Oct 15, 2023
38965f7
[skip ci] correct seed
soranjh Oct 15, 2023
945e22d
[skip ci] modify docstring
soranjh Oct 15, 2023
96edcdd
correct math
soranjh Oct 15, 2023
cd735d1
[skip ci] modify docs
soranjh Oct 15, 2023
e8f433c
Added error computation
Jaybsoni Oct 17, 2023
c52774b
adding base tests
Jaybsoni Oct 18, 2023
bcbe8bb
Added more integration tests
Jaybsoni Oct 18, 2023
5e2f998
lint
Jaybsoni Oct 18, 2023
092c20d
Merge branch 'master' into qdrift
Jaybsoni Oct 18, 2023
623f3b7
update changelog
soranjh Oct 19, 2023
ef6437d
update image
soranjh Oct 19, 2023
ceea397
add grad tests
Jaybsoni Oct 19, 2023
110e822
add interface tests for execution
Jaybsoni Oct 19, 2023
d84b04c
Merge branch 'master' into qdrift
Jaybsoni Oct 19, 2023
2cfa58f
Added tests
Jaybsoni Oct 19, 2023
48f8066
final tests
Jaybsoni Oct 19, 2023
63a0c1e
Merge branch 'master' into qdrift
Jaybsoni Oct 19, 2023
464057c
fix error func + lint
Jaybsoni Oct 19, 2023
7a89ce7
lint
Jaybsoni Oct 19, 2023
6152f4d
Merge branch 'master' into qdrift
Jaybsoni Oct 19, 2023
a7b3864
lint + code review commentsa
Jaybsoni Oct 20, 2023
bb71916
lint docstring
Jaybsoni Oct 20, 2023
c676250
lint
Jaybsoni Oct 20, 2023
34c6cd2
Merge branch 'master' into qdrift
Jaybsoni Oct 20, 2023
559ee72
remove flaky import
Jaybsoni Oct 20, 2023
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
1 change: 1 addition & 0 deletions pennylane/templates/subroutines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@
from .basis_rotation import BasisRotation
from .qsvt import QSVT, qsvt
from .select import Select
from .qdrift import QDrift
227 changes: 227 additions & 0 deletions pennylane/templates/subroutines/qdrift.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# 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.
"""
Contains template for QDrift subroutine.
"""
import pennylane as qml
from pennylane.operation import Operation
from pennylane.ops import Sum
import numpy as np
Jaybsoni marked this conversation as resolved.
Show resolved Hide resolved

class QDrift(Operation):
r"""An operation representing the QDrift subroutine for the complex matrix exponential
of a given Hamiltonian.

The QDrift subroutine provides a method to approximate the matrix exponential of hamiltonian
expressed as a linear combination of terms which in general do not commute. Consider the hamiltonian
:math:`H = \Sigma^{N}_{j=1} h_j H_{j}`, the product formula is constructed by random sampling over the terms
of the Hamiltonian. With probability :math:`p_j` we will add to the product the operator
:math:`\exp{(\frac{i \lambda H_j}{N})}`, where :math:`\lambda = \sum_{j=1}^{N} |h_j|`.
On the other hand, :math:`p_j` is calculated as :math:`p_j = \frac{|h_j|}{\lambda}`.
The number of terms to be added to the product will be entered in the class constructor.

Args:
hamiltonian (Union[~.Hamiltonian, ~.Sum]): The Hamiltonian of the system.
time (complex): The time for which the system evolves.

Keyword Args:
n (int): The number of terms to be added to the product formula. Default is 1.
Jaybsoni marked this conversation as resolved.
Show resolved Hide resolved
seed (int): The seed for the random number generator. Default is 42.
Raises:
TypeError: The 'hamiltonian' is not of type ~.Hamiltonian, or ~.Sum.
ValueError: One or more of the terms in 'hamiltonian' are not Hermitian.
ValueError: The number of samples 'n' is not an integer.

**Example**

.. code-block:: python3

coeffs = [0.25, 0.75]
ops = [qml.PauliX(0), qml.PauliZ(0)]
H = qml.dot(coeffs, ops)

dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
def my_circ():
# Prepare some state
qml.Hadamard(0)

# Evolve according to H
qml.QDrift(H, time=1.2, n = 10)

# Measure some quantity
return qml.probs()

>>> my_circ()
[0.52825127 0. 0.47174873 0. ]


.. details::
:title: Usage Details
Jaybsoni marked this conversation as resolved.
Show resolved Hide resolved

We can also compute the gradient with respect to the coefficients of the hamiltonian and the
evolution time:

.. code-block:: python3

@qml.qnode(dev)
def my_circ(time):
# Prepare H:
H = qml.dot([0.2, -0.1], [qml.PauliY(0), qml.PauliZ(1)])

# Prepare some state
qml.Hadamard(0)

# Evolve according to H
qml.QDrift(H, time, n=10, seed=10)

# Measure some quantity
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))


>>> args = qnp.array([1.23])
Jaybsoni marked this conversation as resolved.
Show resolved Hide resolved
>>> print(qml.grad(my_circ)(*tuple(args)))
0.27980654844422853
"""

def __init__( # pylin: disable=too-many-arguments

Check notice on line 98 in pennylane/templates/subroutines/qdrift.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane/templates/subroutines/qdrift.py#L98

Too many arguments (6/5) (too-many-arguments)
self, hamiltonian, time, n=1, seed=None, id=None
):
r"""Initialize the QDrift class"""

if isinstance(hamiltonian, qml.Hamiltonian):
coeffs, ops = hamiltonian.terms()
hamiltonian = qml.dot(coeffs, ops)

if not isinstance(hamiltonian, Sum):
raise TypeError(
f"The given operator must be a PennyLane ~.Hamiltonian or ~.Sum got {hamiltonian}"
)

self._hyperparameters = {
"n": n,
"seed": seed,
"base": hamiltonian,
}
super().__init__(time, wires=hamiltonian.wires, id=id)

def _flatten(self):
Jaybsoni marked this conversation as resolved.
Show resolved Hide resolved
"""Serialize the operation into trainable and non-trainable components.

Returns:
data, metadata: The trainable and non-trainable components.

See ``Operator._unflatten``.

The data component can be recursive and include other operations. For example, the trainable component of ``Adjoint(RX(1, wires=0))``
will be the operator ``RX(1, wires=0)``.

The metadata **must** be hashable. If the hyperparameters contain a non-hashable component, then this
method and ``Operator._unflatten`` should be overridden to provide a hashable version of the hyperparameters.

**Example:**

>>> op = qml.Rot(1.2, 2.3, 3.4, wires=0)
>>> qml.Rot._unflatten(*op._flatten())
Rot(1.2, 2.3, 3.4, wires=[0])
>>> op = qml.PauliRot(1.2, "XY", wires=(0,1))
>>> qml.PauliRot._unflatten(*op._flatten())
PauliRot(1.2, XY, wires=[0, 1])

Operators that have trainable components that differ from their ``Operator.data`` must implement their own
``_flatten`` methods.

>>> op = qml.ctrl(qml.U2(3.4, 4.5, wires="a"), ("b", "c") )
>>> op._flatten()
((U2(3.4, 4.5, wires=['a']),),
(<Wires = ['b', 'c']>, (True, True), <Wires = []>))
"""
hamiltonian = self.hyperparameters["base"]
time = self.parameters[0]

hashable_hyperparameters = tuple(
(key, value) for key, value in self.hyperparameters.items() if key != "base"
)
return (hamiltonian, time), hashable_hyperparameters

@classmethod
def _unflatten(cls, data, metadata):
Jaybsoni marked this conversation as resolved.
Show resolved Hide resolved
"""Recreate an operation from its serialized format.

Args:
data: the trainable component of the operation
metadata: the non-trainable component of the operation.
Jaybsoni marked this conversation as resolved.
Show resolved Hide resolved

The output of ``Operator._flatten`` and the class type must be sufficient to reconstruct the original
operation with ``Operator._unflatten``.

**Example:**

>>> op = qml.Rot(1.2, 2.3, 3.4, wires=0)
>>> op._flatten()
((1.2, 2.3, 3.4), (<Wires = [0]>, ()))
>>> qml.Rot._unflatten(*op._flatten())
>>> op = qml.PauliRot(1.2, "XY", wires=(0,1))
>>> op._flatten()
((1.2,), (<Wires = [0, 1]>, (('pauli_word', 'XY'),)))
>>> op = qml.ctrl(qml.U2(3.4, 4.5, wires="a"), ("b", "c") )
>>> type(op)._unflatten(*op._flatten())
Controlled(U2(3.4, 4.5, wires=['a']), control_wires=['b', 'c'])

"""
hyperparameters_dict = dict(metadata)
return cls(*data, **hyperparameters_dict)

@staticmethod
def compute_decomposition(*args, **kwargs):
Jaybsoni marked this conversation as resolved.
Show resolved Hide resolved
r"""Representation of the operator as a product of other operators (static method).

.. math:: O = O_1 O_2 \dots O_n.

.. note::

Operations making up the decomposition should be queued within the
``compute_decomposition`` method.

.. seealso:: :meth:`~.Operator.decomposition`.

Args:
*params (list): trainable parameters of the operator, as stored in the ``parameters`` attribute
wires (Iterable[Any], Wires): wires that the operator acts on
**hyperparams (dict): non-trainable hyperparameters of the operator, as stored in the ``hyperparameters`` attribute

Returns:
list[Operator]: decomposition of the operator
"""
time = args[0]
n = kwargs["n"]
seed = kwargs["seed"]
ops = kwargs["base"].operands

with qml.QueuingManager.stop_recording():
Jaybsoni marked this conversation as resolved.
Show resolved Hide resolved

Check notice on line 213 in pennylane/templates/subroutines/qdrift.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane/templates/subroutines/qdrift.py#L213

Trailing whitespace (trailing-whitespace)
coeffs = [op.scalar for op in ops]
lmbda = qml.math.sum(qml.math.abs(coeffs))
probs = qml.math.abs(coeffs) / lmbda
exps = [qml.exp(op.base, lmbda * time * 1j / n) for op in ops]

choice_rng = np.random.default_rng(seed=seed)
decomp = choice_rng.choice(exps, p=probs, size=n, replace=True)
Jaybsoni marked this conversation as resolved.
Show resolved Hide resolved


if qml.QueuingManager.recording():
for op in decomp: # apply operators in reverse order of expression
qml.apply(op)

return decomp

Check notice on line 227 in pennylane/templates/subroutines/qdrift.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane/templates/subroutines/qdrift.py#L227

Final newline missing (missing-final-newline)
Loading