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

Hilbert Schmidt templates #2364

Merged
merged 56 commits into from
Apr 14, 2022
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
5d4eed4
Prototype of operation.
rmoyard Mar 22, 2022
00fa3fd
Local
rmoyard Mar 22, 2022
e3907ae
Merge branch 'master' into hilbert_schmidt_test
antalszava Mar 23, 2022
bfcdb81
Update and test frame.
rmoyard Mar 24, 2022
3dee3b1
Merge branch 'master' into hilbert_schmidt_test
rmoyard Apr 5, 2022
f2546ee
Merge branch 'hilbert_schmidt_test' of https://github.com/PennyLaneAI…
rmoyard Apr 5, 2022
aac612a
Update image
rmoyard Apr 5, 2022
5662499
Change image
rmoyard Apr 5, 2022
5578905
Update doc
rmoyard Apr 5, 2022
b54a75f
Merge branch 'master' into hilbert_schmidt_test
rmoyard Apr 5, 2022
2094bef
LHST documentation
rmoyard Apr 5, 2022
70ef921
Add test
rmoyard Apr 5, 2022
8fe65e5
Update
rmoyard Apr 7, 2022
7de5caf
Merge branch 'master' into hilbert_schmidt_test
rmoyard Apr 7, 2022
2635ce6
Add test for errors.
rmoyard Apr 7, 2022
2b6427d
Forgot add
rmoyard Apr 7, 2022
3a16c25
Change doc
rmoyard Apr 7, 2022
c37ce13
Merge branch 'master' into hilbert_schmidt_test
rmoyard Apr 11, 2022
c978edf
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
cd15527
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
51688f5
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
07e9c2d
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
0e75719
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
41efe25
Update tests/templates/test_subroutines/test_hilbert_schmidt.py
rmoyard Apr 14, 2022
bca4f75
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
873c06c
Update tests/templates/test_subroutines/test_hilbert_schmidt.py
rmoyard Apr 14, 2022
baec50d
Update tests/templates/test_subroutines/test_hilbert_schmidt.py
rmoyard Apr 14, 2022
ac596ab
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
1118450
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
17da940
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
f07ce5b
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
05f3a71
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
3255868
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
78fe825
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
db10056
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
0d444fe
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
01b31f4
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
17e922b
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
a2d7c05
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
ccea088
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
9152f17
Update tests/templates/test_subroutines/test_hilbert_schmidt.py
rmoyard Apr 14, 2022
4a8dbdf
Update tests/templates/test_subroutines/test_hilbert_schmidt.py
rmoyard Apr 14, 2022
c19afa8
Update tests/templates/test_subroutines/test_hilbert_schmidt.py
rmoyard Apr 14, 2022
7101dd7
Update from review
rmoyard Apr 14, 2022
518e73d
Merge branch 'master' into hilbert_schmidt_test
antalszava Apr 14, 2022
de26a77
Docstring
rmoyard Apr 14, 2022
3c84d87
Quantum function error local
rmoyard Apr 14, 2022
d79fc80
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
8975556
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
c07cde3
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
273bb19
Update pennylane/templates/subroutines/hilbert_schmidt.py
rmoyard Apr 14, 2022
bb0c087
Update
rmoyard Apr 14, 2022
3e28ca1
update
rmoyard Apr 14, 2022
f39f8bf
black
rmoyard Apr 14, 2022
3c958ce
Update
rmoyard Apr 14, 2022
5933ccd
Merge branch 'master' into hilbert_schmidt_test
rmoyard Apr 14, 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
Binary file added doc/_static/templates/subroutines/hst.pdf
Binary file not shown.
Binary file added doc/_static/templates/subroutines/hst.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 16 additions & 1 deletion doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<h3>New features since last release</h3>

* Adds an optimization transform that matches pieces of user-provided identity templates in a circuit and replaces them with an equivalent component.
* Added an optimization transform that matches pieces of user-provided identity templates in a circuit and replaces them with an equivalent component.
[(#2032)](https://github.com/PennyLaneAI/pennylane/pull/2032)

First let's consider the following circuit where we want to replace sequence of two ``pennylane.S`` gates with a
Expand Down Expand Up @@ -54,6 +54,21 @@

For more details on using pattern matching optimization you can check the corresponding documentation and also the
following [paper](https://dl.acm.org/doi/full/10.1145/3498325).

* Added two new templates the `HilbertSchmidt` template and the `LocalHilbertSchmidt` template.
[(#2364)](https://github.com/PennyLaneAI/pennylane/pull/2364)

```python
from pennylane.templates import HilbertSchmidt

with qml.tape.QuantumTape(do_queue=False) as u_tape:
qml.Hadamard(wires=0)

def v_function(params):
qml.RZ(params[0], wires=1)

hilbert_template = qml.HilbertSchmidt(v_params, v_function=v_function, v_wires=[1], u_tape=u_tape)
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
```

* Added a swap based transpiler transform.
[(#2118)](https://github.com/PennyLaneAI/pennylane/pull/2118)
Expand Down
1 change: 1 addition & 0 deletions pennylane/templates/subroutines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@
from .grover import GroverOperator
from .qft import QFT
from .kupccgsd import kUpCCGSD
from .hilbert_schmidt import HilbertSchmidt, LocalHilbertSchmidt
261 changes: 261 additions & 0 deletions pennylane/templates/subroutines/hilbert_schmidt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
# Copyright 2022 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 submodule contains the templates for the Hilbert-Schmidt tests.
"""
# pylint: disable-msg=too-many-arguments
import pennylane as qml
from pennylane.operation import AnyWires, Operation


class HilbertSchmidt(Operation):
r"""Create a Hilbert-Schmidt template that can be used to compute the Hilbert-Schmidt Test (HST).
rmoyard marked this conversation as resolved.
Show resolved Hide resolved

The HST is a useful quantity used when we want to compile an unitary `U` with an approximate unitary `V`. The HST
is used as a distance between `U` and `V`, the result of executing the HST is 0 if and only if `V` is equal to
`U` (up to a global phase). Therefore we can define a cost by:

.. math::
C_{HST} = 1 - \frac{1}{d^2} \left|Tr(V^{\dagger}U)\right|^2,

where the quantity :math:`\frac{1}{d^2} \left|Tr(V^{\dagger}U)\right|^2` is obtained by executing the
Hilbert-Schmidt Test. It is equivalent to taking the outcome probability of the state :math:`|0 ... 0\rangle`
for the following circuit:

.. figure:: ../../_static/templates/subroutines/hst.png
:align: center
:width: 80%
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
:target: javascript:void(0);

It defines our decomposition for the Hilbert-Schmidt Test template.

Args:
params (array): Parameters for the quantum function `V`.
v_function (callable): Quantum function that represents the approximate compiled unitary `V`.
v_wires (int or Iterable[Number, str]]): The wire(s) the approximate compiled unitary act on.
u_tape (.QuantumTape): `U`, the unitary to be compiled as a ``qml.tape.QuantumTape``.

Raises:
QuantumFunctionError:

* The argument ``u_tape`` must be a ``QuantumTape``.
* ``v_function`` is not a valid quantum function.
* ``U`` and ``V`` do not have the same number of wires.
* The wires ``v_wires`` are a subset of ``V`` wires.
* ``u_tape`` and ``v_tape`` must act on distinct wires.

.. Reference::

[1] Sumeet Khatri, Ryan LaRose, Alexander Poremba, Lukasz Cincio, Andrew T. Sornborger and Patrick J. Coles
Quantum-assisted Quantum Compiling.
`arxiv/1807.00800 <https://arxiv.org/pdf/1807.00800.pdf>`_

.. UsageDetails::

Consider that we want to evaluate the Hilbert Schmidt Test cost between the unitary ``U`` and an approximate
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
unitary ``V``. We need to define some functions where it is possible to use the :class:`~.HilbertSchmidt`
template. Here the considered unitary is ``Hadamard`` and we try to compute the cost for the approximate
unitary ``RZ``. For an angle that is equal to ``0`` (``Identity``), we have the maximal cost which is ``1``.

.. code-block:: python

import pennylane as qml
from pennylane.templates import HilbertSchmidt
rmoyard marked this conversation as resolved.
Show resolved Hide resolved

with qml.tape.QuantumTape(do_queue=False) as u_tape:
qml.Hadamard(wires=0)

def v_function(params):
qml.RZ(params[0], wires=1)

dev = qml.device("default.qubit", wires=2)

@qml.qnode(dev)
def hilbert_test(v_params, v_function, v_wires, u_tape):
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
qml.HilbertSchmidt(v_params, v_function=v_function, v_wires=v_wires, u_tape=u_tape)
return qml.probs(u_tape.wires + v_wires)

def cost_hst(parameters, v_function, v_wires, u_tape):
return (1 - hilbert_test(v_params=parameters, v_function=v_function, v_wires=v_wires, u_tape=u_tape)[0])

Now that the cost function has been defined it can be called for specific parameters:

>>> cost_hst([0], v_function = v_function, v_wires = [1], u_tape = u_tape)
1

"""
num_wires = AnyWires
grad_method = None

def __init__(self, *params, v_function, v_wires, u_tape, do_queue=True, id=None):

self._num_params = len(params)

if not isinstance(u_tape, qml.tape.QuantumTape):
raise qml.QuantumFunctionError("The argument u_tape must be a QuantumTape.")

u_wires = u_tape.wires
self.hyperparameters["u_tape"] = u_tape

if not callable(v_function):
raise qml.QuantumFunctionError(
"The argument v_function must be a callable quantum function."
)

self.hyperparameters["v_function"] = v_function

v_tape = qml.transforms.make_tape(v_function)(*params)
self.hyperparameters["v_tape"] = v_tape
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
self.hyperparameters["v_wires"] = v_tape.wires

if len(u_wires) != len(v_wires):
raise qml.QuantumFunctionError("U and V must have the same number of wires.")

if not qml.wires.Wires(v_wires).contains_wires(v_tape.wires):
raise qml.QuantumFunctionError("All wires in v_tape must be in v_wires.")
rmoyard marked this conversation as resolved.
Show resolved Hide resolved

# Intersection of wires
if len(qml.wires.Wires.shared_wires([u_tape.wires, v_tape.wires])) != 0:
raise qml.QuantumFunctionError("u_tape and v_tape must act on distinct wires.")

wires = qml.wires.Wires(u_wires + v_wires)

super().__init__(*params, wires=wires, do_queue=do_queue, id=id)

@property
def num_params(self):
return self._num_params

@staticmethod
def compute_decomposition(
params, wires, u_tape, v_tape, v_function=None, v_wires=None
): # pylint: disable=arguments-differ,unused-argument
r"""Representation of the operator as a product of other operators."""
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
n_wires = len(u_tape.wires + v_tape.wires)
decomp_ops = []

first_range = range(0, int(n_wires / 2))
second_range = range(int(n_wires / 2), n_wires)

# Hadamard first layer
for i in first_range:
decomp_ops.append(qml.Hadamard(wires[i]))

# CNOT first layer
for i, j in zip(first_range, second_range):
decomp_ops.append(qml.CNOT(wires=[wires[i], wires[j]]))
rmoyard marked this conversation as resolved.
Show resolved Hide resolved

# Unitary U
for op_u in u_tape.operations:
# The operation has been defined outside of this function, to queue it we call qml.apply.
qml.apply(op_u)
decomp_ops.append(op_u)

# Unitary V conjugate
for op_v in v_tape.operations:
decomp_ops.append(op_v.adjoint())

# CNOT second layer
for i, j in zip(reversed(first_range), reversed(second_range)):
decomp_ops.append(qml.CNOT(wires=[wires[i], wires[j]]))

# Hadamard second layer
for i in first_range:
decomp_ops.append(qml.Hadamard(wires[i]))
return decomp_ops

def adjoint(self): # pylint: disable=arguments-differ
adjoint_op = HilbertSchmidt(
*self.parameters,
u_tape=self.hyperparameters["u_tape"],
v_function=self.hyperparameters["v_function"],
v_wires=self.hyperparameters["v_wires"],
)
adjoint_op.inverse = not self.inverse
return adjoint_op


class LocalHilbertSchmidt(HilbertSchmidt):
r"""Create a Local Hilbert Schmidt template that can be used to compute the Local Hilbert Schmidt Test (LHST).
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
The result of the LHST is a useful quantity for compiling a unitary ``U`` with an approximate unitary ``V``. The
LHST is used as a distance between `U` and `V`, it is similar to the Hilbert schmidt test but the measurement is
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
rmoyard marked this conversation as resolved.
Show resolved Hide resolved
made only on one qubit at the end of the circuit. The LHST cost is always smaller than the HST cost and is useful
for large unitaries.

Args:
params (array): Parameters for the quantum function `V`.
v_function (Callable): Quantum function that represents the approximate compiled unitary `V`.
v_wires (int or Iterable[Number, str]]): the wire(s) the approximate compiled unitary act on.
u_tape (.QuantumTape): `U`, the unitary to be compiled as a ``qml.tape.QuantumTape``.

Raises:
QuantumFunctionError:

* The argument u_tape must be a QuantumTape.
* ``v_function`` is not a valid Quantum function.
* `U` and `V` do not have the same number of wires.
* The wires ``v_wires`` are a subset of `V` wires.
* u_tape and v_tape must act on distinct wires.

**Reference**

[1] Sumeet Khatri, Ryan LaRose, Alexander Poremba, Lukasz Cincio, Andrew T. Sornborger and Patrick J. Coles
Quantum-assisted Quantum Compiling.
`arxiv/1807.00800 <https://arxiv.org/pdf/1807.00800.pdf>`_
"""

@staticmethod
def compute_decomposition(
params, wires, u_tape, v_tape, v_function=None, v_wires=None
): # pylint: disable=arguments-differ,unused-argument
r"""Representation of the operator as a product of other operators (static method)."""
decomp_ops = []
n_wires = len(u_tape.wires + v_tape.wires)
first_range = range(0, int(n_wires / 2))
second_range = range(int(n_wires / 2), n_wires)

# Hadamard first layer
for i in first_range:
decomp_ops.append(qml.Hadamard(wires[i]))

# CNOT first layer
for i, j in zip(first_range, second_range):
decomp_ops.append(qml.CNOT(wires=[wires[i], wires[j]]))

# Unitary U
for op_u in u_tape.operations:
qml.apply(op_u)
decomp_ops.append(op_u)

# Unitary V conjugate
for op_v in v_tape.operations:
decomp_ops.append(op_v.adjoint())

# Only one CNOT
decomp_ops.append(qml.CNOT(wires=[wires[0], wires[int(n_wires / 2)]]))

# Only one Hadamard
decomp_ops.append(qml.Hadamard(wires[0]))

return decomp_ops

def adjoint(self): # pylint: disable=arguments-differ
adjoint_op = LocalHilbertSchmidt(
*self.parameters,
u_tape=self.hyperparameters["u_tape"],
v_function=self.hyperparameters["v_function"],
v_wires=self.hyperparameters["v_wires"],
)
adjoint_op.inverse = not self.inverse
return adjoint_op
Loading