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

Add operator transform functionality #2240

Merged
merged 23 commits into from
Mar 2, 2022
Merged

Add operator transform functionality #2240

merged 23 commits into from
Mar 2, 2022

Conversation

josh146
Copy link
Member

@josh146 josh146 commented Feb 28, 2022

Context:

Currently, it is complex and unwieldy to write new functions that apply to operators in PennyLane, for several reasons:

  • We aim to support multiple UIs, including applying the function directly to an instantiated operator,

    op = some_function(op)

    as well as a functional approach

    op = some_function(qml.Operation)(params, wires)
  • The function must know how to deal with both a single Operator object, as well as multiple operators in the form of a tape

  • The function must be usable both as a decorator and inline

  • The function could support optional parameters that determine its usage

  • operator functions sometimes need to take into account wire ordering

All of this results in a big barrier to contributions, while also requiring that contributors know about the internal implementation details of a lot of PennyLane's core.

In addition, for functions of operators that have already been implemented (e.g., get_unitary_matrix(), adjoint(), ctrl()), the same code boilerplate that enables the above UI is often repeated, not sufficiently tested, and error prone.

Description of the Change:

  • Abstracts away all of the boilerplate required to construct the above UI into a single, well tested and well documented decorator @qml.op_transform.

  • Decorating a function of an operator with qml.op_transform enables it to be used as per the context section, with minimal overhead to the developer.

  • Wire ordering is taken into account automatically, and depends on the object the transform is applied to. For tapes and qfuncs, the wires available on the tape define the wire ordering. For QNodes, the wires available on the device define the wire ordering.

  • By default, it creates transforms that accept a single operator. However, you can also register how the transform acts on a tape of multiple operators. Once this is defined, the transform can be used anywhere in PennyLane --- at the operator level for operator arithmetic, or at the qfunc/qnode level.

  • There is no restriction to what the transform returns; it can return another operator, or an arbitrary object such as a matrix or a string. E.g., draw(), trace(), matrix() etc. can all be rewritten using this framework.

  • Operator transforms that return operators are special -- they are also qfunc transforms, since the output of the operator transform will always be quantum in nature. As a result, they can be used within PennyLane's compilation frameworks.

Benefits: Will standardize a UI pattern that has been long assumed, ensure it is well tested, and make it a lot easier for contributors to contribute functions that act on operators.

Possible Drawbacks:

  • The name

  • Possible confusion with the other transform decorators.

Once this PR is merged, I plan to revisit the transform decorators, and see if they can be unified under a single @qml.transform decorator that smartly knows which decorator to use under the hood.

Related GitHub Issues: n/a

@github-actions
Copy link
Contributor

Hello. You may have forgotten to update the changelog!
Please edit doc/releases/changelog-dev.md with:

  • A one-to-two sentence description of the change. You may include a small working example for new features.
  • A link back to this PR.
  • Your name (or GitHub username) in the contributors section.

@codecov
Copy link

codecov bot commented Feb 28, 2022

Codecov Report

Merging #2240 (f86736b) into master (cd4cd43) will increase coverage by 2.67%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2240      +/-   ##
==========================================
+ Coverage   96.60%   99.28%   +2.67%     
==========================================
  Files         232      233       +1     
  Lines       18713    18808      +95     
==========================================
+ Hits        18078    18673     +595     
+ Misses        635      135     -500     
Impacted Files Coverage Δ
pennylane/__init__.py 100.00% <ø> (ø)
pennylane/transforms/__init__.py 100.00% <100.00%> (ø)
pennylane/transforms/op_transforms.py 100.00% <100.00%> (ø)
pennylane/_qubit_device.py 98.72% <0.00%> (+0.31%) ⬆️
pennylane/interfaces/batch/tensorflow_autograph.py 100.00% <0.00%> (+1.17%) ⬆️
pennylane/interfaces/batch/tensorflow.py 100.00% <0.00%> (+2.04%) ⬆️
pennylane/devices/default_qubit_tf.py 90.16% <0.00%> (+3.27%) ⬆️
pennylane/interfaces/batch/torch.py 100.00% <0.00%> (+3.27%) ⬆️
pennylane/collections/qnode_collection.py 100.00% <0.00%> (+3.50%) ⬆️
pennylane/interfaces/torch.py 100.00% <0.00%> (+4.39%) ⬆️
... and 16 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update cd4cd43...f86736b. Read the comment docs.

@josh146 josh146 added the review-ready 👌 PRs which are ready for review by someone from the core team. label Feb 28, 2022
@josh146 josh146 changed the title [WIP] Add operator transform functionality Add operator transform functionality Feb 28, 2022
raise OperationTransformError(
f"Wires in circuit {tape.wires.tolist()} are inconsistent with "
f"those in wire_order {wire_order.tolist()}"
)
Copy link
Contributor

@mariaschuld mariaschuld Feb 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check may be expensive, but probably not wise to just ignore, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I copied this from get_unitary_matrix, what do you think? Worth removing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't know...maybe leave for now, and sacrifice for speed later!

- Supports datastructures that may contain multiple operations, such as
a tape, QNode, or qfunc.

- Supports being used with a functional transform UI.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the explanation in the PR description, and find it more useful that this one. I wonder if there is an easier way to tell a developer if she needs this transform, and what it does? Maybe with a few in-line examples already up here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! I'd think even just saying that we'd like to allow func(qml.RY)(0.3, wires=0) and not just func(qml.RY(0.3, wires=0)) (which comes "out-of-the-box" with a standard Python function) could be helpful.

The reader could then contemplate what the real diff between the two is and read on for more explanation.

Copy link
Contributor

@antalszava antalszava left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an amazing feature to have! 😍 🎆 It will be so useful.

I've left comments. The functionality seems to work well and is very nicely tested! The logic is a bit challenging to follow at times and the UI itself could still do with some thinking. Could it maybe be broken down into two separate steps? (op_transform + registration)

pennylane/transforms/op_transforms.py Outdated Show resolved Hide resolved
pennylane/transforms/op_transforms.py Outdated Show resolved Hide resolved
pennylane/transforms/op_transforms.py Outdated Show resolved Hide resolved
Comment on lines 87 to 92
Using ``op_transform`` is not necessary in most cases; simply define a
standard Python function that accepts an operator and returns the
computed quantity.

However, this registration class is useful if you wish to easily create
a function that:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: reading this, how about changing the order?

  1. The cases when op_transform is useful are presented;
  2. There's a note that all the other cases can be done via a standard Python function.

I think, when reading, one is most interested in what the feature could be used for, maybe good to have it first?

- Supports datastructures that may contain multiple operations, such as
a tape, QNode, or qfunc.

- Supports being used with a functional transform UI.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! I'd think even just saying that we'd like to allow func(qml.RY)(0.3, wires=0) and not just func(qml.RY(0.3, wires=0)) (which comes "out-of-the-box" with a standard Python function) could be helpful.

The reader could then contemplate what the real diff between the two is and read on for more explanation.

tests/transforms/test_op_transform.py Show resolved Hide resolved
tests/transforms/test_op_transform.py Outdated Show resolved Hide resolved
tests/transforms/test_op_transform.py Show resolved Hide resolved
tests/transforms/test_op_transform.py Outdated Show resolved Hide resolved
tests/transforms/test_op_transform.py Outdated Show resolved Hide resolved
antalszava and others added 2 commits March 1, 2022 10:37
Comment on lines +31 to +44
r"""Convert a function that applies to operators into a functional transform.

This allows the operator function to be used across PennyLane
on both instantiated operators as well as quantum functions.

By default, this decorator creates functional transforms that
accept a single operator. However, you can also register how the
transform acts on multiple operators. Once this is defined,
the transform can be used anywhere in PennyLane --- at the operator
level for operator arithmetic, or at the qfunc/QNode level.

.. warning::

This is an experimental feature, and is subject to change.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mariaschuld @antalszava I've completely re-written the docstring summary from scratch, let me know if this is better!

@josh146 josh146 requested a review from antalszava March 1, 2022 18:24
Copy link
Contributor

@antalszava antalszava left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me 💯

@josh146 josh146 merged commit d091b90 into master Mar 2, 2022
@josh146 josh146 deleted the op-transform branch March 2, 2022 11:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
review-ready 👌 PRs which are ready for review by someone from the core team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants