Repository: PennyLaneAI/pennylane
Pull request #5194 (2024-02-12 18:48:06)

Use new op math in generator of GlobalPhase and Controlled
**Context:**
The `Tensor` and `Hamiltonian` does not work with `Identity` on no wires, causing issues with `ControlledGlobalPhase` when the `GlobalPhase` acts on no wires.

**Description of the Change:**
1. Use `prod` and `s_prod` in place of straight multiplication in the `generator` of `Controlled` and `GlobalPhase`
2. The generator of `GlobalPhase` now contains an identity that acts on the same wires as the `GlobalPhase`.

**Benefits:**
BugFix

**Related GitHub Issues:**
https://github.com/PennyLaneAI/pennylane/issues/4578

**Related Shortcut Story:**
[sc-44933]

github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [`doc/releases/changelog-dev.md`](/PennyLaneAI/pennylane/blob/master/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[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5194?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`7fd7796`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/7fd7796e1dd9a208900eda8a2063cc2e90c875d6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.68% compared to head [(`a340080`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5194?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.68%.


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #5194      +/-   ##
==========================================
- Coverage   99.68%   99.68%   -0.01%     
==========================================
  Files         395      395              
  Lines       36455    36180     -275     
==========================================
- Hits        36341    36065     -276     
- Misses        114      115       +1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5194?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 765271edf..3a0b7163a 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -494,6 +494,9 @@
   operators have a valid `pauli_rep` property.
   [(#5177)](https://github.com/PennyLaneAI/pennylane/pull/5177)
 
+* Controlled `GlobalPhase` with non-zero control wire no longer throws an error.
+  [(#5194)](https://github.com/PennyLaneAI/pennylane/pull/5194)
+
 * A `QNode` transformed with `mitigate_with_zne` now accepts batch parameters.
   [(#5195)](https://github.com/PennyLaneAI/pennylane/pull/5195)
 
diff --git a/pennylane/ops/identity.py b/pennylane/ops/identity.py
index 31a18af1e..729cb3831 100644
--- a/pennylane/ops/identity.py
+++ b/pennylane/ops/identity.py
@@ -389,5 +389,4 @@ class GlobalPhase(Operation):
         return [GlobalPhase(z * self.data[0], self.wires)]
 
     def generator(self):
-        wires = self.wires or [0]
-        return -1 * qml.Identity(wires)
+        return qml.s_prod(-1, qml.Identity(self.wires))
diff --git a/pennylane/ops/op_math/controlled.py b/pennylane/ops/op_math/controlled.py
index 47758494d..f9ee20dee 100644
--- a/pennylane/ops/op_math/controlled.py
+++ b/pennylane/ops/op_math/controlled.py
@@ -666,11 +666,7 @@ class Controlled(SymbolicOp):
         projectors = (
             qml.Projector([val], wires=w) for val, w in zip(self.control_values, self.control_wires)
         )
-
-        if qml.operation.active_new_opmath():
-            return qml.prod(*projectors, sub_gen)
-
-        return 1.0 * operation.Tensor(*projectors) @ sub_gen
+        return qml.prod(*projectors, sub_gen)
 
     @property
     def has_adjoint(self):


Test patch:
diff --git a/tests/ops/qubit/test_parametric_ops.py b/tests/ops/qubit/test_parametric_ops.py
index 791a6043d..192c80c4d 100644
--- a/tests/ops/qubit/test_parametric_ops.py
+++ b/tests/ops/qubit/test_parametric_ops.py
@@ -2007,18 +2007,19 @@ class TestGrad:
 
     @pytest.mark.autograd
     @pytest.mark.parametrize("dev_name,diff_method", device_methods)
-    def test_globalphase_autograd_grad(self, tol, dev_name, diff_method):
+    @pytest.mark.parametrize("wires", [(0, 1), (1, 0)])
+    def test_globalphase_autograd_grad(self, tol, dev_name, diff_method, wires):
         """Test the gradient with Autograd for a controlled GlobalPhase."""
 
         dev = qml.device(dev_name, wires=2)
 
         @qml.qnode(dev, diff_method=diff_method)
         def circuit(x):
-            qml.Identity(0)
-            qml.Hadamard(1)
-            qml.ctrl(qml.GlobalPhase(x), 1)
-            qml.Hadamard(1)
-            return qml.expval(qml.PauliZ(1))
+            qml.Identity(wires[0])
+            qml.Hadamard(wires[1])
+            qml.ctrl(qml.GlobalPhase(x), control=wires[1])
+            qml.Hadamard(wires[1])
+            return qml.expval(qml.PauliZ(wires[1]))
 
         phi = npp.array(2.1, requires_grad=True)
 
@@ -2351,7 +2352,8 @@ class TestGrad:
 
     @pytest.mark.tf
     @pytest.mark.parametrize("dev_name,diff_method", device_methods)
-    def test_globalphase_tf_grad(self, tol, dev_name, diff_method):
+    @pytest.mark.parametrize("wires", [(0, 1), (1, 0)])
+    def test_globalphase_tf_grad(self, tol, dev_name, diff_method, wires):
         """Test the gradient with Tensorflow for a controlled GlobalPhase."""
 
         import tensorflow as tf
@@ -2360,11 +2362,11 @@ class TestGrad:
 
         @qml.qnode(dev, diff_method=diff_method)
         def circuit(x):
-            qml.Identity(0)
-            qml.Hadamard(1)
-            qml.ctrl(qml.GlobalPhase(x), 1)
-            qml.Hadamard(1)
-            return qml.expval(qml.PauliZ(1))
+            qml.Identity(wires[0])
+            qml.Hadamard(wires[1])
+            qml.ctrl(qml.GlobalPhase(x), control=wires[1])
+            qml.Hadamard(wires[1])
+            return qml.expval(qml.PauliZ(wires[1]))
 
         phi = tf.Variable(2.1, dtype=tf.complex128)
 
@@ -2505,7 +2507,8 @@ class TestGrad:
 
     @pytest.mark.jax
     @pytest.mark.parametrize("dev_name,diff_method", device_methods)
-    def test_globalphase_jax_grad(self, tol, dev_name, diff_method):
+    @pytest.mark.parametrize("wires", [(1, 0), (0, 1)])
+    def test_globalphase_jax_grad(self, tol, dev_name, diff_method, wires):
         """Test the gradient with JAX for a controlled GlobalPhase."""
 
         import jax
@@ -2517,11 +2520,11 @@ class TestGrad:
 
         @qml.qnode(dev, diff_method=diff_method)
         def circuit(x):
-            qml.Identity(0)
-            qml.Hadamard(1)
-            qml.ctrl(qml.GlobalPhase(x), 1)
-            qml.Hadamard(1)
-            return qml.expval(qml.PauliZ(1))
+            qml.Identity(wires[0])
+            qml.Hadamard(wires[1])
+            qml.ctrl(qml.GlobalPhase(x), control=wires[1])
+            qml.Hadamard(wires[1])
+            return qml.expval(qml.PauliZ(wires[1]))
 
         phi = jnp.array(2.1)
 
@@ -2532,7 +2535,8 @@ class TestGrad:
 
     @pytest.mark.torch
     @pytest.mark.parametrize("dev_name,diff_method", device_methods)
-    def test_globalphase_torch_grad(self, tol, dev_name, diff_method):
+    @pytest.mark.parametrize("wires", [(1, 0), (0, 1)])
+    def test_globalphase_torch_grad(self, tol, dev_name, diff_method, wires):
         """Test the gradient with Torch for a controlled GlobalPhase."""
 
         import torch
@@ -2541,11 +2545,11 @@ class TestGrad:
 
         @qml.qnode(dev, diff_method=diff_method)
         def circuit(x):
-            qml.Identity(0)
-            qml.Hadamard(1)
-            qml.ctrl(qml.GlobalPhase(x), 1)
-            qml.Hadamard(1)
-            return qml.expval(qml.PauliZ(1))
+            qml.Identity(wires[0])
+            qml.Hadamard(wires[1])
+            qml.ctrl(qml.GlobalPhase(x), control=wires[1])
+            qml.Hadamard(wires[1])
+            return qml.expval(qml.PauliZ(wires[1]))
 
         phi = torch.tensor(2.1, requires_grad=True, dtype=torch.float64)