Skip to content

Commit

Permalink
Decomposition of arbitrary two-qubit unitaries. (#1552)
Browse files Browse the repository at this point in the history
* Initial commit.

* Add scaffolding and helper function.

* Temp commit.

* Progress!!

* Update su2su2 implementation.

* Temp commit.

* Implementation that essentially works. Need testing.

* Remove perms.

* Works about half the time.

* Tidy a bit.

* Small tweaks.

* Temporary work.

* Working version, minus tests.

* Fix compute matrix in tests. Add check for tensor product structure.

* Move decomposition tests to decomposition.

* Update some tests. Add to docs.

* Remove old test file.

* Run black.

* Apply suggestions from code review

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Make E matrix numpy constant.

* Vectorize permutation matrix construction.

* Replace column negation with matrix multiplication.

* Fix decomposition to work in all interfaces. Fix tests to match.

* qml.math to math

* Remove unneeded variables.

* Calculate the required number of CNOTs from tr(gammaU).

* Split into multiple cases. 0, 2, and 3-CNOT cases are currently functional.

* Add broken version.

* Update with integration tests.

* Improve documentation.

* Run black.

* Fix matrix ordering.

* RUn black.

* Apply suggestions from code review

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Update error message. Change all qml.math to math.

* Fix tests.

* Change error to warning.

* Remove unused warnings.

* Run black.

* Test CNOT number computation.

* Fix bug in zyz decomposition and tidy tests.

* Update CHANGELOG.

* Tweak comments.

* Add single-CNOT case.

* Separate out utility functions and clean up file.

* Cast to 1j to handle square root of negative values.

* Partially working 2-CNOT case.

* Revert qnode file.

* Fix CHANGELOG.

* Fix CHANGELOG.

* Revert test files.

* Revert test_qnode.

* Run black.

* Temporary commit.

* Working 2-CNOT case!

* Run black.

* It works!

* Fix test.

* Documentation and code factor fixes.

* Tweak documentation.

* Apply suggestions from code review

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Move functions back from utils to module.

* Make ASCII circuits in docstrings pretty.

* Make single-qubit constant matrices top-level.

* Rearrange CNOT check. Move matrix to top level.

* Streamline conversion to SO(4).

* Remove LAST_COL_NEG.

* Fix conj.

* Update docs and add developer notes.

* Update CHANGELOG.

* Fix CHANGELOG.

* Fix docstring.

* Fix decomposition so expansion works.

* Fix QMC test.

* Fix transform so expand works.

* Remove unused import.

* Add length checks.

* Apply suggestions from code review

* Update changelog.

* Remove parameter shift test case.

* Tweak docstring.

* Apply suggestions from code review

Co-authored-by: Josh Izaac <josh146@gmail.com>

* Add SU(4) invalid check.

* Add test case for 3-qubit QubitUnitary in unitary_to_rot.

* Massive simplification.

Co-authored-by: Olivia Di Matteo <dimatteo.olivia@gmail.com>
Co-authored-by: Josh Izaac <josh146@gmail.com>
  • Loading branch information
3 people authored Sep 16, 2021
1 parent 1bfabb9 commit 3871332
Show file tree
Hide file tree
Showing 13 changed files with 3,308 additions and 42 deletions.
743 changes: 743 additions & 0 deletions doc/_static/two_qubit_decomposition_2_cnots.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
813 changes: 813 additions & 0 deletions doc/_static/two_qubit_decomposition_3_cnots.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 37 additions & 1 deletion doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,42 @@

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

* Arbitrary two-qubit unitaries can now be decomposed into elementary gates. This
functionality has been incorporated into the `qml.transforms.unitary_to_rot` transform, and is
available separately as `qml.transforms.two_qubit_decomposition`.
[(#1552)](https://github.com/PennyLaneAI/pennylane/pull/1552)

As an example, consider the following randomly-generated matrix and circuit that uses it:

```python
U = np.array([
[-0.03053706-0.03662692j, 0.01313778+0.38162226j, 0.4101526 -0.81893687j, -0.03864617+0.10743148j],
[-0.17171136-0.24851809j, 0.06046239+0.1929145j, -0.04813084-0.01748555j, -0.29544883-0.88202604j],
[ 0.39634931-0.78959795j, -0.25521689-0.17045233j, -0.1391033 -0.09670952j, -0.25043606+0.18393466j],
[ 0.29599198-0.19573188j, 0.55605806+0.64025769j, 0.06140516+0.35499559j, 0.02674726+0.1563311j ]
])

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

@qml.qnode(dev)
@qml.transforms.unitary_to_rot
def circuit(x, y):
qml.RX(x, wires=0)
qml.QubitUnitary(U, wires=[0, 1])
qml.RY(y, wires=0)
return qml.expval(qml.PauliZ(wires=0))
```

If we run the circuit, we can see the new decomposition:

```pycon
>>> circuit(0.3, 0.4)
tensor(-0.70520073, requires_grad=True)
>>> print(qml.draw(circuit)(0.3, 0.4))
0: ──RX(0.3)─────────────────Rot(-3.5, 0.242, 0.86)──╭X──RZ(0.176)───╭C─────────────╭X──Rot(5.56, 0.321, -2.09)───RY(0.4)──┤ ⟨Z⟩
1: ──Rot(-1.64, 2.69, 1.58)──────────────────────────╰C──RY(-0.883)──╰X──RY(-1.47)──╰C──Rot(-1.46, 0.337, 0.587)───────────┤
```

* The transform for the Jacobian of the classical preprocessing within a QNode,
`qml.transforms.classical_jacobian`, now takes a keyword argument `argnum` to specify
the QNode argument indices with respect to which the Jacobian is computed.
Expand Down Expand Up @@ -107,4 +143,4 @@

This release contains contributions from (in alphabetical order):

Josh Izaac, Christina Lee, David Wierichs.
Olivia Di Matteo, Josh Izaac, Christina Lee, David Wierichs.
9 changes: 8 additions & 1 deletion pennylane/ops/qubit/matrix_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,14 @@ def decomposition(U, wires):
decomp_ops = qml.transforms.decompositions.zyz_decomposition(U, wire)
return decomp_ops

raise NotImplementedError("Decompositions only supported for single-qubit unitaries")
if qml.math.shape(U) == (4, 4):
wires = Wires(wires)
decomp_ops = qml.transforms.two_qubit_decomposition(U, wires)
return decomp_ops

raise NotImplementedError(
"Decompositions only supported for single- and two-qubit unitaries."
)

def adjoint(self):
return QubitUnitary(qml.math.T(qml.math.conj(self.matrix)), wires=self.wires)
Expand Down
3 changes: 2 additions & 1 deletion pennylane/transforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
:toctree: api
~transforms.zyz_decomposition
~transforms.two_qubit_decomposition
Transforms that act on tapes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -105,7 +106,7 @@
from .classical_jacobian import classical_jacobian
from .compile import compile
from .control import ControlledOperation, ctrl
from .decompositions import zyz_decomposition
from .decompositions import zyz_decomposition, two_qubit_decomposition
from .draw import draw
from .hamiltonian_expand import hamiltonian_expand
from .invisible import invisible
Expand Down
1 change: 1 addition & 0 deletions pennylane/transforms/decompositions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
"""

from .single_qubit_unitary import zyz_decomposition
from .two_qubit_unitary import two_qubit_decomposition
Loading

0 comments on commit 3871332

Please sign in to comment.