Repository: PennyLaneAI/pennylane (python)

Issue:
[BUG] `Controlled.has_sparse_matrix` is incorrect for ops without matrices or sparse matrices
### Expected behavior

`Operator.has_sparse_matrix` should agree with whether or not an actual matrix is returned by `Operator.sparse_matrix`

### Actual behavior

`Controlled.has_sparse_matrix` is always true.

### Additional information

_No response_

### Source code

```shell
>>> op = qml.ctrl(qml.Permute(np.array([1, 0, 2]), (0,1, 2)), (3,4))
>>> op.has_sparse_matrix
True
```

### Tracebacks

```shell

```

### System information

```shell
master
```

### Existing GitHub issues

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

Pull request description:
fix `qml.Controlled.has_sparse_matrix`
**Context:**

`Controlled.has_sparse_matrix` was always true

**Description of the Change:**

Fix `Controlled.has_sparse_matrix`.

**Benefits:**'

`Controlled.has_sparse_matrix` is accurate.

Unblock https://github.com/PennyLaneAI/pennylane/pull/6883/

**Possible Drawbacks:**

**Related GitHub Issues:**

Fixes #7024 [sc-85452]


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index deab8eef8..388202a98 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -426,6 +426,10 @@
 
 <h3>Bug fixes 🐛</h3>
 
+* `qml.ops.Controlled.has_sparse_matrix` now provides the correct information
+  by checking if the target operator has a sparse or dense matrix defined.
+  [(#7025)](https://github.com/PennyLaneAI/pennylane/pull/7025)
+
 * `qml.capture.PlxprInterpreter` now flattens pytree arguments before evaluation.
   [(#6975)](https://github.com/PennyLaneAI/pennylane/pull/6975)
 
diff --git a/pennylane/ops/op_math/controlled.py b/pennylane/ops/op_math/controlled.py
index 3e0d7de53..fd9b4f1f7 100644
--- a/pennylane/ops/op_math/controlled.py
+++ b/pennylane/ops/op_math/controlled.py
@@ -681,6 +681,10 @@ class Controlled(SymbolicOp):
         wire_order = wire_order or self.wires
         return qml.math.expand_matrix(canonical_matrix, wires=self.wires, wire_order=wire_order)
 
+    @property
+    def has_sparse_matrix(self):
+        return self.base.has_sparse_matrix or self.base.has_matrix
+
     # pylint: disable=arguments-differ
     def sparse_matrix(self, wire_order=None, format="csr"):
         try:


Test patch:
diff --git a/tests/ops/op_math/test_controlled.py b/tests/ops/op_math/test_controlled.py
index 41a49e5ec..c8d7430a7 100644
--- a/tests/ops/op_math/test_controlled.py
+++ b/tests/ops/op_math/test_controlled.py
@@ -742,6 +742,7 @@ class TestMatrix:
         sparse_mat = op.sparse_matrix()
         assert isinstance(sparse_mat, sparse.csr_matrix)
         assert qml.math.allclose(sparse_mat.toarray(), op.matrix())
+        assert op.has_sparse_matrix
 
     @pytest.mark.parametrize("control_values", ([0, 0, 0], [0, 1, 0], [0, 1, 1], [1, 1, 1]))
     def test_sparse_matrix_only_matrix_defined(self, control_values):
@@ -754,6 +755,7 @@ class TestMatrix:
         sparse_mat = op.sparse_matrix()
         assert isinstance(sparse_mat, sparse.csr_matrix)
         assert qml.math.allclose(op.sparse_matrix().toarray(), op.matrix())
+        assert op.has_sparse_matrix
 
     def test_sparse_matrix_wire_order(self):
         """Check if the user requests specific wire order, sparse_matrix() returns the same as matrix()."""
@@ -772,6 +774,7 @@ class TestMatrix:
 
         base = TempOperator(1)
         op = Controlled(base, 2)
+        assert not op.has_sparse_matrix
 
         with pytest.raises(qml.operation.SparseMatrixUndefinedError):
             op.sparse_matrix()