-
Notifications
You must be signed in to change notification settings - Fork 575
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
Add convenience decorators for creating qfunc transforms #1315
Changes from 47 commits
44b4c37
05e4eba
6a061ae
8f9c792
99f1b05
a86595a
766e126
3a3af5a
5a17b32
48733d8
4535d4e
b15d16d
def8edc
9c2c2c6
85fa341
762c7ca
c30e7f7
3a826df
44e4f71
8c27fd1
9c3be12
f3cde4e
f121e09
0c280fc
1e37727
fc7df1a
1647f50
c275cb9
4ad490b
20cdba0
d064fc3
18efc2c
a7646d7
83e1e9d
aeb8d76
40f7884
192e2a1
cceed13
3e50255
cbfdb3c
b982228
5ec3422
bdf8876
82027bc
3add7a9
c668550
21b779a
8726032
c2d4204
acba2d9
b31fa34
dbc8708
39f77da
8afc1b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,66 @@ | |
|
||
<h3>New features since last release</h3> | ||
|
||
* Adds a decorator `@qml.qfunc_transform` to easily create a transformation | ||
that modifies the behaviour of a quantum function. | ||
[(#1315)](https://github.com/PennyLaneAI/pennylane/pull/1315) | ||
|
||
For example, consider the following transform, which replaces all | ||
`CRX` gates with a sequence of `RX`, `RY`, and `CZ` gates: | ||
|
||
```python | ||
@qml.qfunc_transform | ||
def my_transform(tape, x, y): | ||
for op in tape.operations + tape.measurements: | ||
if op.name == "CRX": | ||
wires = op.wires | ||
param = op.parameters[0] | ||
qml.RX(x * param, wires=wires[1]) | ||
qml.RY(y * qml.math.sqrt(param), wires=wires[1]) | ||
qml.CZ(wires=[wires[1], wires[0]]) | ||
else: | ||
op.queue() | ||
josh146 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
|
||
We can now apply this transform to any quantum function: | ||
|
||
```python | ||
dev = qml.device("default.qubit", wires=2) | ||
|
||
def ansatz(x): | ||
qml.Hadamard(wires=0) | ||
qml.CRX(x, wires=[0, 1]) | ||
|
||
@qml.qnode(dev) | ||
def circuit(param, transform_weights): | ||
qml.RX(0.1, wires=0) | ||
|
||
# apply the transform to the ansatz | ||
my_transform(*transform_weights)(ansatz)(param) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a very confusing example, or maybe just confusing syntax. Why are we transforming the first of the two parameters? I would expect something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree in that it does come a bit out of left-field 😆 Funnily enough, my intuition prior to working on this PR was slightly different - I was expecting something along the lines of my_transform(ansatz, transform_weights)(param) (in that the The reason instead for my_transform(transform_weights)(ansatz)(param) Is due almost completely to how decorators work in Python; decorators must always be of the form # 1. create a transform defined by `transform_weights`
specific_transform = my_transform(transform_weights)
# 2. Apply the transform (callable -> callable) to the qfunc
new_qfunc = specific_transform(new_qfunc)
# 3. evaluate the new, transformed, quantum function
new_qfunc(params) If we decide to drop the decorator functionality, then we could definitely support my_transform(ansatz, transform_weights)(param) @glassnotes, what do you think?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm in favour of the decorators because of how much they abstract away in terms of writing the actual transform. However we should definitely add the example you wrote above to the docs, to show the equivalence of the inline version vs. "expanded" version. |
||
|
||
return qml.expval(qml.PauliZ(1)) | ||
``` | ||
|
||
We can print this QNode to show that the qfunc transform is taking place: | ||
|
||
```pycon | ||
>>> x = np.array(0.5, requires_grad=True) | ||
>>> y = np.array([0.1, 0.2], requires_grad=True) | ||
>>> print(qml.draw(circuit)(x, y)) | ||
0: ──RX(0.1)───H──────────╭Z──┤ | ||
1: ──RX(0.05)──RY(0.141)──╰C──┤ ⟨Z⟩ | ||
``` | ||
|
||
Evaluating the QNode, as well as the derivative, with respect to the gate | ||
parameter *and* the transform weights: | ||
|
||
```pycon | ||
>>> circuit(x, y) | ||
0.9887793925354269 | ||
>>> qml.grad(circuit)(x, y) | ||
(array(-0.02485651), array([-0.02474011, -0.09954244])) | ||
``` | ||
|
||
* Added validation for noise channel parameters. Invalid noise parameters now | ||
raise a `ValueError`. [(#1357)](https://github.com/PennyLaneAI/pennylane/pull/1357) | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,66 +1,80 @@ | ||||||
# Copyright 2018-2021 Xanadu Quantum Technologies Inc. | ||||||
josh146 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
# 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 subpackage contains QNode, quantum function, device, and tape transforms. | ||||||
|
||||||
|
||||||
.. currentmodule:: pennylane | ||||||
|
||||||
QNode transforms | ||||||
---------------- | ||||||
|
||||||
The following transforms act on QNodes. They return new transformed functions | ||||||
that compute the desired quantity. | ||||||
|
||||||
.. autosummary:: | ||||||
:toctree: api | ||||||
|
||||||
~transforms.classical_jacobian | ||||||
~draw | ||||||
~metric_tensor | ||||||
|
||||||
Quantum function transforms | ||||||
--------------------------- | ||||||
|
||||||
The following transforms act on quantum functions (Python functions | ||||||
containing quantum operations) that are used *inside* QNodes. | ||||||
|
||||||
.. autosummary:: | ||||||
:toctree: api | ||||||
|
||||||
~adjoint | ||||||
~ctrl | ||||||
~transforms.invisible | ||||||
|
||||||
Tape transforms | ||||||
--------------- | ||||||
|
||||||
The following transforms act on quantum tapes, and return one or | ||||||
more tapes as well as a classical processing function. | ||||||
|
||||||
.. autosummary:: | ||||||
:toctree: api | ||||||
|
||||||
~transforms.measurement_grouping | ||||||
~transforms.metric_tensor_tape | ||||||
~transforms.hamiltonian_expand | ||||||
""" | ||||||
from .adjoint import adjoint | ||||||
from .classical_jacobian import classical_jacobian | ||||||
from .control import ControlledOperation, ctrl | ||||||
from .draw import draw | ||||||
from .invisible import invisible | ||||||
from .measurement_grouping import measurement_grouping | ||||||
from .metric_tensor import metric_tensor, metric_tensor_tape | ||||||
from .hamiltonian_expand import hamiltonian_expand | ||||||
# 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 subpackage contains QNode, quantum function, device, and tape transforms. | ||||||
|
||||||
|
||||||
.. currentmodule:: pennylane | ||||||
|
||||||
QNode transforms | ||||||
---------------- | ||||||
|
||||||
The following transforms act on QNodes. They return new transformed functions | ||||||
that compute the desired quantity. | ||||||
|
||||||
.. autosummary:: | ||||||
:toctree: api | ||||||
|
||||||
~transforms.classical_jacobian | ||||||
~draw | ||||||
~metric_tensor | ||||||
|
||||||
Quantum function transforms | ||||||
--------------------------- | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Would it make sense, while things are in flux, to just remove the headers that name the type of transforms, and keep instead just the one-line description that explains how they work? This keeps transforms with similar behaviour grouped, but doesn't bind us to a specific name yet. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||||||
|
||||||
The following transforms act on quantum functions (Python functions | ||||||
containing quantum operations) that are used *inside* QNodes. | ||||||
|
||||||
.. autosummary:: | ||||||
:toctree: api | ||||||
|
||||||
~adjoint | ||||||
~ctrl | ||||||
~transforms.invisible | ||||||
|
||||||
Tape transforms | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do these contents need to be updated to fit the new description of "tape transform", or will that be done as part of #1362? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh good question. I think I have been avoiding updating the docs for now, since this is so fluid.... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that tape transforms themselves are in a reasonably final form, with the only factor left being the multiple tape case. That said, since the focus and decorator here is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Though I guess if we did that we would have to move the three transforms in the autosummary to a different section 😕 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm kinda tempted to leave it very loosely defined for now, to avoid locking us in to anything too early. Instead:
That is, rather than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, okay I'm on board with that! |
||||||
--------------- | ||||||
|
||||||
The following transforms act on quantum tapes, and return one or | ||||||
more tapes as well as a classical processing function. | ||||||
|
||||||
.. autosummary:: | ||||||
:toctree: api | ||||||
|
||||||
~transforms.measurement_grouping | ||||||
~transforms.metric_tensor_tape | ||||||
~transforms.hamiltonian_expand | ||||||
|
||||||
Decorators and utility functions | ||||||
-------------------------------- | ||||||
|
||||||
The following decorators and convenience functions are provided | ||||||
to help build custom QNode, quantum function, and tape transforms: | ||||||
|
||||||
.. autosummary:: | ||||||
:toctree: api | ||||||
|
||||||
~single_tape_transform | ||||||
~qfunc_transform | ||||||
~transforms.make_tape | ||||||
""" | ||||||
from .adjoint import adjoint | ||||||
from .classical_jacobian import classical_jacobian | ||||||
from .control import ControlledOperation, ctrl | ||||||
from .draw import draw | ||||||
from .hamiltonian_expand import hamiltonian_expand | ||||||
from .invisible import invisible | ||||||
from .measurement_grouping import measurement_grouping | ||||||
from .metric_tensor import metric_tensor, metric_tensor_tape | ||||||
from .qfunc_transforms import make_tape, single_tape_transform, qfunc_transform |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does it take a
tape
if it's a "qfunc" transform?I thought "qfunc" transforms took in quantum functions and returned quantum functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@albi3ro that's exactly right! You could always write a qfunc transform manually like so, without ever needing to use any decorators:
Note that this is pseudocode - steps (1) and (3) are much more complicated in practice than they are presented here! It's actually quite non-trivial to get the above working in general, because it requires:
an intimate knowledge of the queuing system and how it works
how to convert qfuncs into tapes without them being queued
dealing with potentially nested queuing contexts
having to take into account that qfuncs can be called in lots of different places
Steps (1) and (3) are actually the same no matter the qfunc transform; really, it is only step (2),
tape_transform
and the corresponding tape transform parameters, that really define the qfunc transformation.So the idea with the decorator is therefore:
In essence, you can think of the decorator as taking a
tape_transform
, and elevating it into a qfunc transform.