Repository: PennyLaneAI/pennylane
Pull request #4768 (2023-11-01 16:07:54)

State that `Evolution` has matrix if target is `Hamiltonian`
We discovered a difference between `default.qubit` and `default.qubit.legacy` over their treatment of the evolution of hamiltonians:

```python
dev = qml.device('default.qubit', wires=3)
# dev = qml.device('default.qubit.legacy', wires=3)  # this works!
​
coeffs = [0.1, 0.2]
operators = [qml.PauliZ(0) @ qml.PauliZ(1), qml.PauliZ(0) @ qml.PauliZ(2)]
hamiltonian = qml.Hamiltonian(coeffs, operators)
​
@qml.qnode(dev)
def f():
    qml.evolve(hamiltonian, 0.5)
    return qml.density_matrix([0])
​
f()
```

Previously `qml.evolve(hamiltonain, t)` worked because `Evolution` was hardcoded as an accepted observable. This meant it became an accepted operation as well, even though it stated it didn't have a matrix.

In `ScalarSymbolicOp`, the parent for `Evolution`, we added a special logical pathway for the matrix if the target is a Hamiltonian, but we didn't include that special logical pathway for the `has_matrix` property.


This PR updates the `has_matrix` property for and allows the evolution of a hamiltonian to execute on `default.qubit`.

**Warning:**

This matrix is not backprop compatible.

```
dev = qml.device('default.qubit.legacy', wires=3)

coeffs = [0.1, 0.2]
operators = [qml.PauliZ(0) @ qml.PauliZ(1), qml.PauliZ(0) @ qml.PauliZ(2)]

@qml.qnode(dev)
def f(x):
    hamiltonian = qml.Hamiltonian(x, operators)
    qml.evolve(hamiltonian, 0.5)
    return qml.density_matrix([0])

qml.grad(f)(qml.numpy.array(coeffs))
```
Gives
```
AttributeError: 'ArrayBox' object has no attribute 'tocsr'
```

`qml.dot` and operator arithemetic does not have these problems.


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 46eaa574a..e61965d71 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -40,6 +40,10 @@
 
 <h3>Bug fixes 🐛</h3>
 
+* Any `ScalarSymbolicOp`, like `Evolution`, now states that it has a matrix if the target
+  is a `Hamiltonian`.
+  [(#4768)](https://github.com/PennyLaneAI/pennylane/pull/4768)
+
 <h3>Contributors ✍️</h3>
 
 This release contains contributions from (in alphabetical order):
@@ -47,6 +51,7 @@ This release contains contributions from (in alphabetical order):
 Amintor Dusko,
 Lillian Frederiksen,
 Ankit Khandelwal,
+Christina Lee,
 Anurav Modak,
 Matthew Silverman,
 David Wierichs,
diff --git a/pennylane/ops/op_math/symbolicop.py b/pennylane/ops/op_math/symbolicop.py
index b3cd3de7d..3ad23f2e7 100644
--- a/pennylane/ops/op_math/symbolicop.py
+++ b/pennylane/ops/op_math/symbolicop.py
@@ -195,6 +195,10 @@ class ScalarSymbolicOp(SymbolicOp):
             )
         return scalar_size
 
+    @property
+    def has_matrix(self):
+        return self.base.has_matrix or isinstance(self.base, qml.Hamiltonian)
+
     @property
     def hash(self):
         return hash(


Test patch:
diff --git a/tests/ops/op_math/test_symbolic_op.py b/tests/ops/op_math/test_symbolic_op.py
index c70e34128..dc86a30a2 100644
--- a/tests/ops/op_math/test_symbolic_op.py
+++ b/tests/ops/op_math/test_symbolic_op.py
@@ -139,6 +139,13 @@ class TestProperties:
         op = SymbolicOp(base)
         assert op.has_matrix == has_mat
 
+    def test_has_matrix_hamiltonian(self):
+        """Test that it has a matrix if the base is a hamiltonian."""
+
+        H = qml.Hamiltonian([1.0], [qml.PauliX(0)])
+        op = TempScalar(H, 2)
+        assert op.has_matrix
+
     @pytest.mark.parametrize("is_herm", (True, False))
     def test_is_hermitian(self, is_herm):
         """Test that symbolic op is hermitian if the base is hermitian."""