Repository: PennyLaneAI/pennylane

Issue:
[BUG] Incorrect control values in `ControlledQubitUnitary.pow()`
### Expected behavior

For some integer `x` and 2x2 unitary matrix `mat`, using `ControlledQubitUnitary.pow()` has the following expected behaviour:
```pycon
>>> op = qml.ControlledQubitUnitary(mat, control_wires=[0, 1], wires=2, control_values="00")
>>> pow_ops = op.pow(x)
>>> np.allclose(np.linalg.matrix_power(mat, x), pow_ops[0].data[0])
True
>>> pow_ops[0].hyperparameters["control_values"]
00
```

### Actual behavior

For some integer `x` and 2x2 unitary matrix `mat`, using `ControlledQubitUnitary.pow()` has the following **actual** behaviour:
```pycon
>>> op = qml.ControlledQubitUnitary(mat, control_wires=[0, 1], wires=2, control_values="00")
>>> pow_ops = op.pow(x)
>>> np.allclose(np.linalg.matrix_power(mat, x), pow_ops[0].data[0])
True
>>> pow_ops[0].hyperparameters["control_values"]
None
```


### Additional information

This is because the `pow` method doesn't update the `control_values` hyperparameter for the returned `ControlledQubitUnitary` list.

### Source code

_No response_

### Tracebacks

_No response_

### System information

```shell
This isn't included because the issue is related to an incorrect implementation of ControlledQubitUnitary.pow()
```


### Existing GitHub issues

- [X] I have searched existing GitHub issues to make sure the issue does not already exist.


Discussion:
Hi @mudit2812, thank you for letting us know about this bug! Would you be interested in working on a fix?

Resolving patch:
diff --git a/pennylane/ops/qubit/matrix_ops.py b/pennylane/ops/qubit/matrix_ops.py
--- a/pennylane/ops/qubit/matrix_ops.py
+++ b/pennylane/ops/qubit/matrix_ops.py
@@ -361,12 +361,19 @@ def compute_matrix(
     def control_wires(self):
         return self.hyperparameters["control_wires"]
 
+    @property
+    def control_values(self):
+        """str.  Specifies whether or not to control on zero "0" or one "1" for each
+        control wire."""
+        return self.hyperparameters["control_values"]
+
     def pow(self, z):
         if isinstance(z, int):
             return [
                 ControlledQubitUnitary(
                     qml.math.linalg.matrix_power(self.data[0], z),
                     control_wires=self.control_wires,
+                    control_values=self.control_values,
                     wires=self.hyperparameters["u_wires"],
                 )
             ]
@@ -374,8 +381,7 @@ def pow(self, z):
 
     def _controlled(self, wire):
         ctrl_wires = self.control_wires + wire
-        old_control_values = self.hyperparameters["control_values"]
-        values = None if old_control_values is None else f"{old_control_values}1"
+        values = None if self.control_values is None else f"{self.control_values}1"
         new_op = ControlledQubitUnitary(
             *self.parameters,
             control_wires=ctrl_wires,


Test patch:
diff --git a/tests/ops/qubit/test_matrix_ops.py b/tests/ops/qubit/test_matrix_ops.py
--- a/tests/ops/qubit/test_matrix_ops.py
+++ b/tests/ops/qubit/test_matrix_ops.py
@@ -817,13 +817,16 @@ def test_pow(self, n):
             ]
         )
 
-        op = qml.ControlledQubitUnitary(U1, control_wires=("b", "c"), wires="a")
+        op = qml.ControlledQubitUnitary(
+            U1, control_wires=("b", "c"), wires="a", control_values="01"
+        )
 
         pow_ops = op.pow(n)
         assert len(pow_ops) == 1
 
         assert pow_ops[0].hyperparameters["u_wires"] == op.hyperparameters["u_wires"]
         assert pow_ops[0].control_wires == op.control_wires
+        assert pow_ops[0].control_values == op.control_values
 
         op_mat_to_pow = qml.math.linalg.matrix_power(op.data[0], n)
         assert qml.math.allclose(pow_ops[0].data[0], op_mat_to_pow)