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

Implement transform for full single-qubit gate fusion #1458

Merged
merged 61 commits into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
0406efc
Copy over some files from 1384.
Jul 8, 2021
8560c1d
Add optimization utils file.
Jul 8, 2021
68f9e79
Add updated qubit ops file.
Jul 8, 2021
09a3ae1
Update init file.
Jul 8, 2021
eba5f51
Streamline utils file.
Jul 8, 2021
5781743
Update operation.py.
Jul 8, 2021
7a21d41
Add arctan, and add JAX 64-bit support to test.
Jul 8, 2021
7118152
Remove as_rot_angles.
Jul 8, 2021
b156b70
Update docstrings.
Jul 8, 2021
0a9abe9
Merge branch 'master' into ch7104-implement-transforms-to-cancel-redu…
glassnotes Jul 8, 2021
c99f3cd
Merge branch 'ch7104-implement-transforms-to-cancel-redundant' of htt…
Jul 8, 2021
76947be
Shorten variable names for better readability.
Jul 8, 2021
2b04ec2
Update default values of compilation tags.
glassnotes Jul 12, 2021
95a61a9
Update CHANGELOG.
glassnotes Jul 12, 2021
af55d17
Add new transforms to docs.
glassnotes Jul 12, 2021
86f4f5e
Merge branch 'master' into ch7104-implement-transforms-to-cancel-redu…
glassnotes Jul 12, 2021
2f78671
Apply suggestions from code review
glassnotes Jul 13, 2021
0f20fa9
Add support for cancelling adjacent adjoint gates.
glassnotes Jul 14, 2021
c26d14b
Improve docstrings for compilation attributes. Add symmetric over con…
glassnotes Jul 14, 2021
2ecb449
Adjust Toffoli test case.
glassnotes Jul 14, 2021
f7fb6c7
Convert utility functions to non-private.
glassnotes Jul 14, 2021
dd73732
Add tests for utility functions.
glassnotes Jul 14, 2021
a783181
Add optional parameters for rotation merging.
glassnotes Jul 14, 2021
2b29cf0
Streamline angle fusion for 0 cases.
glassnotes Jul 14, 2021
82688ac
Disable warning for too many return statements.
glassnotes Jul 14, 2021
be50d8d
Apply suggestions from code review
glassnotes Jul 14, 2021
d960e87
Add tests for optimization utils.
glassnotes Jul 15, 2021
7ce969f
Merge branch 'ch7104-implement-transforms-to-cancel-redundant' of htt…
glassnotes Jul 15, 2021
20198e7
Add transform for full single-qubit fusion.
glassnotes Jul 15, 2021
16f8e8d
Add example to CHANGELOG.
glassnotes Jul 15, 2021
622f4cd
Fix indentation in CHANGELOG.
glassnotes Jul 15, 2021
33333d8
Merge branch 'ch7104-implement-transforms-to-cancel-redundant' into c…
glassnotes Jul 15, 2021
b81be2a
Fix grammar.
glassnotes Jul 15, 2021
cdc3326
Adjust docstring.
glassnotes Jul 15, 2021
cb02556
Update .github/CHANGELOG.md
glassnotes Jul 15, 2021
2cf1062
Update .github/CHANGELOG.md
glassnotes Jul 15, 2021
121ec6f
Add exception handling. Add missing gates to tests.
glassnotes Jul 15, 2021
62c394b
Merge branch 'ch7105-implement-transform-for-full-single-qubit' of ht…
glassnotes Jul 15, 2021
1f584f2
Update .github/CHANGELOG.md
josh146 Jul 16, 2021
06965cf
Apply suggestions from code review
glassnotes Jul 16, 2021
a262b14
Rename as_rot_angles to single_qubit_rot_angles.
glassnotes Jul 16, 2021
2c30e6a
Clarify documentation and update single_qubit_rot_angles name.
glassnotes Jul 16, 2021
c867c42
Add option for gate exclusion.
glassnotes Jul 16, 2021
5fbd0e4
Rename specify_ops to include_gates.
glassnotes Jul 16, 2021
2f741fd
Remove spurious print statement.
glassnotes Jul 16, 2021
b472282
Apply suggestions from code review
glassnotes Jul 16, 2021
056350e
Set rtol and clarify fusion is for two or more gates in a row.
glassnotes Jul 16, 2021
df6b181
Fix test.
glassnotes Jul 16, 2021
c39c36a
Update handling of tolerance.
glassnotes Jul 16, 2021
b282724
Merge branch 'ch7104-implement-transforms-to-cancel-redundant' into c…
glassnotes Jul 16, 2021
4e748dd
Update pennylane/transforms/optimization/single_qubit_fusion.py
glassnotes Jul 16, 2021
5c9b4db
Update pennylane/transforms/optimization/single_qubit_fusion.py
glassnotes Jul 16, 2021
ac052ee
Merge branch 'master' into ch7105-implement-transform-for-full-single…
josh146 Jul 19, 2021
7993658
Fix codefactor issues. Simplify some logic and add final test case fo…
glassnotes Jul 19, 2021
6ce3f82
Merge branch 'ch7105-implement-transform-for-full-single-qubit' of ht…
glassnotes Jul 19, 2021
449b343
Disable too-many-branches.
glassnotes Jul 19, 2021
0fc444b
Merge branch 'master' into ch7105-implement-transform-for-full-single…
glassnotes Jul 19, 2021
ce19bdd
Update docstring. Add tests for single_qubit_rot_angles.
glassnotes Jul 19, 2021
fe64b63
Merge branch 'ch7105-implement-transform-for-full-single-qubit' of ht…
glassnotes Jul 19, 2021
f94ea21
Add missing tests to complete coverage.
glassnotes Jul 19, 2021
142ab05
Merge branch 'master' into ch7105-implement-transform-for-full-single…
glassnotes Jul 20, 2021
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
88 changes: 88 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,94 @@

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

* A new quantum function transform has been added to perform full fusion of
arbitrary-length sequences of single-qubit gates.
[(#1458)](https://github.com/PennyLaneAI/pennylane/pull/1458)

The `single_qubit_fusion` transform acts on all sequences of
single-qubit operations in a quantum function, and converts each
sequence to a single `Rot` gate. For example given the circuit:

```python
def circuit(x, y, z):
qml.Hadamard(wires=0)
qml.PauliZ(wires=1)
qml.RX(x, wires=0)
qml.RY(y, wires=1)
qml.CZ(wires=[1, 0])
qml.T(wires=0)
qml.SX(wires=0)
qml.Rot(x, y, z, wires=1)
qml.Rot(z, y, x, wires=1)
return qml.expval(qml.PauliX(wires=0))
```

```pycon
>>> optimized_circuit = qml.transforms.single_qubit_fusion()(circuit)
>>> dev = qml.device('default.qubit', wires=2)
>>> qnode = qml.QNode(optimized_circuit, dev)
>>> print(qml.draw(qnode)(0.1, 0.2, 0.3))
0: ──Rot(3.24, 1.57, 0)──╭Z──Rot(2.36, 1.57, -1.57)────┤ ⟨X⟩
1: ──Rot(3.14, 0.2, 0)───╰C──Rot(0.406, 0.382, 0.406)──┤
```
Copy link
Member

Choose a reason for hiding this comment

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

🙌


* Two new quantum function transforms have been added to enable the
removal of redundant gates in quantum circuits.
[(#1455)](https://github.com/PennyLaneAI/pennylane/pull/1455)

The `cancel_inverses` transform loops through a list of operations,
and removes adjacent pairs of operations that cancel out. For example,

```python
def circuit():
qml.Hadamard(wires=0)
qml.PauliZ(wires=1)
qml.Hadamard(wires=0)
qml.T(wires=0)
qml.CZ(wires=[0, 1])
qml.CZ(wires=[1, 0])
return qml.expval(qml.PauliX(wires=0))
```

```pycon
>>> dev = qml.device('default.qubit', wires=2)
>>> qnode = qml.QNode(circuit, dev)
>>> print(qml.draw(qnode)())
0: ──H──H──T──╭C──╭Z──┤ ⟨X⟩
1: ──Z────────╰Z──╰C──┤
>>> optimized_circuit = qml.transforms.cancel_inverses(circuit)
>>> optimized_qnode = qml.QNode(optimized_circuit, dev)
>>> print(qml.draw(optimized_qnode)())
0: ──T──┤ ⟨X⟩
1: ──Z──┤
```

The `merge_rotations` transform combines adjacent rotation gates of
the same type into a single gate, including controlled rotations.

```python
def circuit(x, y, z):
qml.RX(x, wires=0)
qml.RX(x, wires=0)
qml.Rot(x, y, z, wires=1)
qml.Rot(y, z, x, wires=1)
qml.CRY(y, wires=[0, 1])
qml.CRY(y + z, wires=[0, 1])
return qml.expval(qml.PauliX(wires=0))
```

```pycon
>>> qnode = qml.QNode(circuit, dev)
>>> print(qml.draw(qnode)(0.1, 0.2, 0.3))
0: ──RX(0.1)─────────────RX(0.1)─────────────╭C────────╭C────────┤ ⟨X⟩
1: ──Rot(0.1, 0.2, 0.3)──Rot(0.2, 0.3, 0.1)──╰RY(0.2)──╰RY(0.5)──┤
>>> optimized_circuit = qml.transforms.merge_rotations()(circuit)
>>> optimized_qnode = qml.QNode(optimized_circuit, dev)
>>> print(qml.draw(optimized_qnode)(0.1, 0.2, 0.3))
0: ──RX(0.2)───────────────────╭C────────┤ ⟨X⟩
1: ──Rot(0.409, 0.485, 0.306)──╰RY(0.7)──┤
```

* A decomposition has been added to ``QubitUnitary`` that makes the
single-qubit case fully differentiable in all interfaces. Furthermore,
a quantum function transform, ``unitary_to_rot()``, has been added to decompose all
Expand Down
5 changes: 5 additions & 0 deletions pennylane/math/single_dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,14 @@ def _take_autograd(tensor, indices, axis=None):
ar.autoray._SUBMODULE_ALIASES["tensorflow", "arcsin"] = "tensorflow.math"
ar.autoray._SUBMODULE_ALIASES["tensorflow", "arccos"] = "tensorflow.math"
ar.autoray._SUBMODULE_ALIASES["tensorflow", "arctan"] = "tensorflow.math"
ar.autoray._SUBMODULE_ALIASES["tensorflow", "arctan2"] = "tensorflow.math"
ar.autoray._SUBMODULE_ALIASES["tensorflow", "diag"] = "tensorflow.linalg"


ar.autoray._FUNC_ALIASES["tensorflow", "arcsin"] = "asin"
ar.autoray._FUNC_ALIASES["tensorflow", "arccos"] = "acos"
ar.autoray._FUNC_ALIASES["tensorflow", "arctan"] = "atan"
ar.autoray._FUNC_ALIASES["tensorflow", "arctan2"] = "atan2"
ar.autoray._FUNC_ALIASES["tensorflow", "diag"] = "diag"


Expand Down Expand Up @@ -243,6 +245,9 @@ def _scatter_element_add_tf(tensor, index, value):
),
)

ar.autoray._SUBMODULE_ALIASES["torch", "arctan2"] = "torch"
ar.autoray._FUNC_ALIASES["torch", "arctan2"] = "atan2"


def _take_torch(tensor, indices, axis=None):
"""Torch implementation of np.take"""
Expand Down
55 changes: 55 additions & 0 deletions pennylane/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,61 @@ def grad_method(self):
s_1]=[-1/2, 1, -\pi/2]` is assumed for every parameter.
"""

# Attributes for compilation transforms
is_self_inverse = None
"""bool or None: ``True`` if the operation is its own inverse.

If ``None``, all instances of the given operation will be ignored during
compilation transforms involving inverse cancellation.
"""

is_symmetric_over_all_wires = None
"""bool or None: ``True`` if the operation is the same if you exchange the order
of wires.

For example, ``qml.CZ(wires=[0, 1])`` has the same effect as ``qml.CZ(wires=[1,
0])`` due to symmetry of the operation.

If ``None``, all instances of the operation will be ignored during
compilation transforms that check for wire symmetry.
"""

is_symmetric_over_control_wires = None
"""bool or None: ``True`` if the operation is the same if you exchange the order
of all but the last wire.

For example, ``qml.Toffoli(wires=[0, 1, 2])`` has the same effect as
``qml.Toffoli(wires=[1, 0, 2])``, but neither are the same as
``qml.Toffoli(wires=[0, 2, 1])``.

If ``None``, all instances of the operation will be ignored during
compilation transforms that check for control-wire symmetry.
"""

is_composable_rotation = None
"""bool or None: ``True`` if composing multiple copies of the operation
results in an addition (or alternative accumulation) of parameters.

For example, ``qml.RZ`` is a composable rotation. Applying ``qml.RZ(0.1,
wires=0)`` followed by ``qml.RZ(0.2, wires=0)`` is equivalent to performing
a single rotation ``qml.RZ(0.3, wires=0)``.

If set to ``None``, the operation will be ignored during compilation
transforms that merge adjacent rotations.
"""

@property
def single_qubit_rot_angles(self):
"""The parameters required to implement a single-qubit gate as an
equivalent ``Rot`` gate, up to a global phase.

Returns:
tuple[float, float, float]: A list of values :math:`[\phi, \theta, \omega]`
such that :math:`RZ(\omega) RY(\theta) RZ(\phi)` is equivalent to the
original operation.
"""
raise NotImplementedError

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is indeed covered in test_single_qubit_fusion_not_implemented using the PauliRot gate.

Copy link
Member

Choose a reason for hiding this comment

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

👍

def get_parameter_shift(self, idx, shift=np.pi / 2):
"""Multiplier and shift for the given parameter, based on its gradient recipe.

Expand Down
Loading