Repository: PennyLaneAI/pennylane

Issue:
[unitaryhack] Add the CPhase operation
*This issue has been tagged for contributions during [unitaryHACK](https://unitaryfund.github.io/unitaryhack/participating-projects.html)*

**Context**
We would like to define a new diagonal operation called `CPhase`, see [further details here](https://qiskit.org/documentation/stubs/qiskit.circuit.library.CPhaseGate.html).

**Steps to take**

This would involve the following steps to be completed:

*A. Adding the operation*

1. Adding the `CPhase` class and its attributes to `pennylane/ops/qubit.py`. This class would inherit from `DiagonalOperation`. Complete its docstring in a similar fashion as for other operations. Make sure the class is accessible through `qml.CPhase`.
2. Add its decomposition: a definition for how the operation could be decomposed into elementary operations,
3. Add its matrix, eigenvalues, adjoint and generator,
4. *Test:* include tests for the operation in the `tests/ops/test_qubit_ops.py` file,

In these steps, having a look at how `class PhaseShift` is defined can help. Also, see [the operation as defined in the `PennyLane-Cirq` plugin](https://github.com/PennyLaneAI/pennylane-cirq/blob/e121db9e1e314b290811256f83722de29cc292f3/pennylane_cirq/ops.py#L142) as a basis.

*B. Adding `default.qubit` support*

5. Add support for the operation in the `default.qubit` device,
6. *Test:* test the operation with `default.qubit` in the `tests/devices/test_default_qubit.py` file.

*Bonus:* Add support to the interface-specific `default.qubit` devices too: `default.qubit.autograd` and `default.qubit.tf`. This will require the operation to be defined in a framework-specific way. These devices inherit the supported operations from `default.qubit`, so if this step is not tackled, the `CPHASE` can be removed from the `operations` dictionary for `default.qubit.autograd` and `default.qubit.tf`.

Discussion:
josh146: Thanks @nahumsa! Let us know as soon as the PR is ready for review, and someone from the team will be on hand to answer any questions/provide a review 🙂 
nahumsa: Hi @josh146. I guess this is ready for a review.
nahumsa: Could someone clarify why the `tests/tape/test_reversible.py::TestGradients::test_gradients[CPhase-PauliX]` would fail? 
It was ok locally 😅 
nahumsa: Is it ok just to remove `qml.CPhase` from `analytic_qubit_ops`, just like the `qml.ControlledPhaseShift`?
codecov[bot]: # [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#1319](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (4d6cd14) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/acec10fc412baea2106cde0e9d1ed1162a430d36?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (acec10f) will **decrease** coverage by `0.03%`.
> The diff coverage is `55.55%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319/graphs/tree.svg?width=650&height=150&src=pr&token=9S3YzlUgGk&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

```diff
@@            Coverage Diff             @@
##           master    #1319      +/-   ##
==========================================
- Coverage   98.16%   98.13%   -0.04%     
==========================================
  Files         148      148              
  Lines       11446    11455       +9     
==========================================
+ Hits        11236    11241       +5     
- Misses        210      214       +4     
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/devices/default\_qubit.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9xdWJpdC5weQ==) | `100.00% <ø> (ø)` | |
| [pennylane/devices/default\_qubit\_autograd.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9xdWJpdF9hdXRvZ3JhZC5weQ==) | `100.00% <ø> (ø)` | |
| [pennylane/devices/default\_qubit\_jax.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9xdWJpdF9qYXgucHk=) | `94.36% <ø> (ø)` | |
| [pennylane/devices/default\_qubit\_tf.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9xdWJpdF90Zi5weQ==) | `90.66% <ø> (ø)` | |
| [pennylane/devices/tf\_ops.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvdGZfb3BzLnB5) | `97.82% <33.33%> (-2.18%)` | :arrow_down: |
| [pennylane/devices/autograd\_ops.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvYXV0b2dyYWRfb3BzLnB5) | `98.66% <50.00%> (-1.34%)` | :arrow_down: |
| [pennylane/devices/jax\_ops.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvamF4X29wcy5weQ==) | `98.64% <50.00%> (-1.36%)` | :arrow_down: |
| [pennylane/ops/qubit.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL29wcy9xdWJpdC5weQ==) | `98.50% <100.00%> (+<0.01%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Last update [acec10f...4d6cd14](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1319?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

antalszava: Hi @nahumsa, thank you for exploring this addition and identifying that we already have an equivalent gate!

Could we create the alias by having
```python
CPhase = ControlledPhaseShift
```
in the `pennylane/pennylane/ops/qubit.py` file? That would essentially mean that `CPhase` is another name to the same class object as `ControlledPhaseShift` and could help with keeping the aliasing short.
nahumsa: Sure! I thought about doing just that, but at the end I think I overcomplicated the solution. 😅 
Sorry about the overcomplication
antalszava: No worries at all! :) Let us know if you'd like us to go for another review round or if any questions come up.
nahumsa: Would it be good to remove the tests as well, or should I keep it?
antalszava: The test cases could be removed and potentially the existing ones that test the eigvals and decomposition of `qml.ControlledPhaseShift` could be parametrized such that:
```python
    @pytest.mark.parametrize("phi", [-0.1, 0.2, 0.5])
    @pytest.mark.parametrize("cphase_op", [qml.ControlledPhaseShift, qml.CPhase])
    def test_controlled_phase_shift_decomp(self, phi):
        op = cphase_op(phi, wires=[0, 1])
        (...)
```
This will make sure that aliasing works fine.
nahumsa: > The test cases could be removed and potentially the existing ones that test the eigvals and decomposition of `qml.ControlledPhaseShift` could be parametrized such that:
> 
> ```python
>     @pytest.mark.parametrize("phi", [-0.1, 0.2, 0.5])
>     @pytest.mark.parametrize("cphase_op", [qml.ControlledPhaseShift, qml.CPhase])
>     def test_controlled_phase_shift_decomp(self, phi):
>         op = cphase_op(phi, wires=[0, 1])
>         (...)
> ```
> 
> This will make sure that aliasing works fine.

Awesome, just added this parameterized test, thanks!
antalszava: Great! :pray: The aliasing could be used in framework-specific files too, so we could assign the `CPhase` name the underlying `ControlledPhaseShift` function in each file to have the same effect. :)
nahumsa: > Great!  The aliasing could be used in framework-specific files too, so we could assign the `CPhase` name the underlying `ControlledPhaseShift` function in each file to have the same effect. :)

Sure. I'll do that.

Patch:
diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index 3aafaae7c..d457c9037 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -1,6 +1,8 @@
 # Release 0.16.0-dev (development release)
 
 <h3>New features since last release</h3>
+* Added CPhase operation as an alias for ControlledPhaseShift operation
+  [(#1319)](https://github.com/PennyLaneAI/pennylane/pull/1319).
 
 * The `qml.Toffoli` operation now has a decomposition over elementary gates. 
   [(#1320)](https://github.com/PennyLaneAI/pennylane/pull/1320)
@@ -258,7 +260,8 @@ random_mat2 = rng.standard_normal(3, requires_grad=False)
 
 This release contains contributions from (in alphabetical order):
 
-Vishnu Ajith, Thomas Bromley, Olivia Di Matteo, Tanya Garg, Diego Guala, Anthony Hayes, Josh Izaac, Brian Shi, Antal Száva, Pavan Jayasinha
+Vishnu Ajith, Thomas Bromley, Olivia Di Matteo, Tanya Garg, Diego Guala,
+Anthony Hayes, Josh Izaac, Pavan Jayasinha, Nahum Sá, Brian Shi, Antal Száva
 
 # Release 0.15.1 (current release)
 
diff --git a/pennylane/devices/default_qubit.py b/pennylane/devices/default_qubit.py
index e5add1007..111630f70 100644
--- a/pennylane/devices/default_qubit.py
+++ b/pennylane/devices/default_qubit.py
@@ -112,6 +112,7 @@ class DefaultQubit(QubitDevice):
         "CZ",
         "PhaseShift",
         "ControlledPhaseShift",
+        "CPhase",
         "RX",
         "RY",
         "RZ",
diff --git a/pennylane/devices/default_qubit_autograd.py b/pennylane/devices/default_qubit_autograd.py
index 6476b66de..8571b8634 100644
--- a/pennylane/devices/default_qubit_autograd.py
+++ b/pennylane/devices/default_qubit_autograd.py
@@ -84,6 +84,7 @@ class DefaultQubitAutograd(DefaultQubit):
     parametric_ops = {
         "PhaseShift": autograd_ops.PhaseShift,
         "ControlledPhaseShift": autograd_ops.ControlledPhaseShift,
+        "CPhase": autograd_ops.ControlledPhaseShift,
         "RX": autograd_ops.RX,
         "RY": autograd_ops.RY,
         "RZ": autograd_ops.RZ,
diff --git a/pennylane/devices/default_qubit_jax.py b/pennylane/devices/default_qubit_jax.py
index e50997381..3d99a1928 100644
--- a/pennylane/devices/default_qubit_jax.py
+++ b/pennylane/devices/default_qubit_jax.py
@@ -137,6 +137,7 @@ class DefaultQubitJax(DefaultQubit):
     parametric_ops = {
         "PhaseShift": jax_ops.PhaseShift,
         "ControlledPhaseShift": jax_ops.ControlledPhaseShift,
+        "CPhase": jax_ops.ControlledPhaseShift,
         "RX": jax_ops.RX,
         "RY": jax_ops.RY,
         "RZ": jax_ops.RZ,
diff --git a/pennylane/devices/default_qubit_tf.py b/pennylane/devices/default_qubit_tf.py
index 694a8b953..b60281d7f 100644
--- a/pennylane/devices/default_qubit_tf.py
+++ b/pennylane/devices/default_qubit_tf.py
@@ -131,6 +131,7 @@ class DefaultQubitTF(DefaultQubit):
     parametric_ops = {
         "PhaseShift": tf_ops.PhaseShift,
         "ControlledPhaseShift": tf_ops.ControlledPhaseShift,
+        "CPhase": tf_ops.ControlledPhaseShift,
         "RX": tf_ops.RX,
         "RY": tf_ops.RY,
         "RZ": tf_ops.RZ,
diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py
index f770a9f79..5b2fff147 100644
--- a/pennylane/ops/qubit.py
+++ b/pennylane/ops/qubit.py
@@ -941,6 +941,9 @@ class ControlledPhaseShift(DiagonalOperation):
         return ControlledPhaseShift(-self.data[0], wires=self.wires)
 
 
+CPhase = ControlledPhaseShift
+
+
 class Rot(Operation):
     r"""Rot(phi, theta, omega, wires)
     Arbitrary single qubit rotation
@@ -3067,6 +3070,7 @@ ops = {
     "RZ",
     "PhaseShift",
     "ControlledPhaseShift",
+    "CPhase",
     "Rot",
     "CRX",
     "CRY",


Test patch:
diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py
index a9f38626e..cac70dfd4 100644
--- a/tests/ops/test_qubit_ops.py
+++ b/tests/ops/test_qubit_ops.py
@@ -376,6 +376,7 @@ class TestOperations:
             qml.Toffoli(wires=[0, 1, 2]),
             qml.PhaseShift(2.133, wires=0),
             qml.ControlledPhaseShift(1.777, wires=[0, 2]),
+            qml.CPhase(1.777, wires=[0, 2]),
             qml.MultiRZ(0.112, wires=[1, 2, 3]),
             qml.CRX(0.836, wires=[2, 3]),
             qml.CRY(0.721, wires=[2, 3]),
@@ -980,10 +981,11 @@ class TestOperations:
         assert np.allclose(decomposed_matrix, opr.matrix)
 
     @pytest.mark.parametrize("phi", [-0.1, 0.2, 0.5])
-    def test_controlled_phase_shift_matrix_and_eigvals(self, phi):
-        """Tests that the ControlledPhaseShift operation calculates the correct matrix and
+    @pytest.mark.parametrize("cphase_op", [qml.ControlledPhaseShift, qml.CPhase])
+    def test_controlled_phase_shift_matrix_and_eigvals(self, phi, cphase_op):
+        """Tests that the ControlledPhaseShift and CPhase operation calculates the correct matrix and
         eigenvalues"""
-        op = qml.ControlledPhaseShift(phi, wires=[0, 1])
+        op = cphase_op(phi, wires=[0, 1])
         res = op.matrix
         exp = ControlledPhaseShift(phi)
         assert np.allclose(res, exp)
@@ -992,9 +994,10 @@ class TestOperations:
         assert np.allclose(res, np.diag(exp))
 
     @pytest.mark.parametrize("phi", [-0.1, 0.2, 0.5])
-    def test_controlled_phase_shift_decomp(self, phi):
-        """Tests that the ControlledPhaseShift operation calculates the correct decomposition"""
-        op = qml.ControlledPhaseShift(phi, wires=[0, 1])
+    @pytest.mark.parametrize("cphase_op", [qml.ControlledPhaseShift, qml.CPhase])
+    def test_controlled_phase_shift_decomp(self, phi, cphase_op):
+        """Tests that the ControlledPhaseShift and CPhase operation calculates the correct decomposition"""
+        op = cphase_op(phi, wires=[0, 1])
         decomp = op.decomposition(phi, wires=[0, 1])
 
         mats = []
diff --git a/tests/tape/test_reversible.py b/tests/tape/test_reversible.py
index a004e968a..6731a74ea 100644
--- a/tests/tape/test_reversible.py
+++ b/tests/tape/test_reversible.py
@@ -282,6 +282,7 @@ class TestGradients:
         qml.CRot,
         qml.PhaseShift,
         qml.ControlledPhaseShift,
+        qml.CPhase,
         qml.PauliRot,
         qml.MultiRZ,
         qml.U1,