Repository: PennyLaneAI/pennylane
Pull request #3902 (2023-03-14 19:07:51)

ParametrizedHamiltonian addition bugfix
**Context:**

The `RydbergHamiltonian` PR (#3749) introduced a change to how coefficients are added when adding ParametrizedHamiltonian, to preserve the order correctly. However, this created a bug when adding a Hamiltonian and a ParametrizedHamiltonian, because one has an array of coefficients, and the other a list, and you can't `array + list` and expect it to append things.

**Description of the Change:**
Now we cast the coefficients to a list so this doesn't happen.

**Related GitHub Issues:**
#3901 

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://codecov.io/gh/PennyLaneAI/pennylane/pull/3902?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#3902](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3902?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (1a119e3) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/7c8859acfad4cede87859d1f8d25448c97dc3147?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (7c8859a) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master    #3902   +/-   ##
=======================================
  Coverage   99.70%   99.70%           
=======================================
  Files         342      342           
  Lines       29853    29853           
=======================================
  Hits        29766    29766           
  Misses         87       87           
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3902?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/pulse/parametrized\_hamiltonian.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3902?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3B1bHNlL3BhcmFtZXRyaXplZF9oYW1pbHRvbmlhbi5weQ==) | `100.00% <100.00%> (ø)` | |

... and [1 file with indirect coverage changes](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3902/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

lillian542: > Is this fix only needed because Hamiltonian doesn't enforce its coeffs to be a List?

Because they don't have to be a list, and because it in fact seems always to be an array when created with the `__mul__` method. Tbh it's not clear to me where/why this is happening. But this is what it does:

```
>>> op = 2 * qml.PauliY(0)
>>> op.coeffs
array([2])
```

```
>>> op = qml.Hamiltonian([2], [qml.PauliY(0)])
>>> op.coeffs
[2]
```

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 748be3a8f..9f4921855 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -150,6 +150,7 @@
 * Fixed bug where the coefficients where not ordered correctly when summing a `ParametrizedHamiltonian`
   with other operators.
   [(#3749)](https://github.com/PennyLaneAI/pennylane/pull/3749)
+  [(#3902)](https://github.com/PennyLaneAI/pennylane/pull/3902)
 
 * The metric tensor transform is fully compatible with Jax and therefore users can provide multiple parameters.
   [(#3847)](https://github.com/PennyLaneAI/pennylane/pull/3847)
diff --git a/pennylane/pulse/parametrized_hamiltonian.py b/pennylane/pulse/parametrized_hamiltonian.py
index 2ffeeacf9..3a3369f8f 100644
--- a/pennylane/pulse/parametrized_hamiltonian.py
+++ b/pennylane/pulse/parametrized_hamiltonian.py
@@ -290,7 +290,8 @@ class ParametrizedHamiltonian:
         coeffs = self.coeffs.copy()
 
         if isinstance(H, (Hamiltonian, ParametrizedHamiltonian)):
-            new_coeffs = coeffs + H.coeffs.copy()
+            # if Hamiltonian, coeffs array must be converted to list
+            new_coeffs = coeffs + list(H.coeffs.copy())
             new_ops = ops + H.ops.copy()
             return ParametrizedHamiltonian(new_coeffs, new_ops)
 
@@ -314,7 +315,8 @@ class ParametrizedHamiltonian:
         coeffs = self.coeffs.copy()
 
         if isinstance(H, (Hamiltonian, ParametrizedHamiltonian)):
-            new_coeffs = H.coeffs.copy() + coeffs
+            # if Hamiltonian, coeffs array must be converted to list
+            new_coeffs = list(H.coeffs.copy()) + coeffs
             new_ops = H.ops.copy() + ops
             return ParametrizedHamiltonian(new_coeffs, new_ops)
 


Test patch:
diff --git a/tests/pulse/test_parametrized_hamiltonian.py b/tests/pulse/test_parametrized_hamiltonian.py
index 52a4daa29..567e38abc 100644
--- a/tests/pulse/test_parametrized_hamiltonian.py
+++ b/tests/pulse/test_parametrized_hamiltonian.py
@@ -57,6 +57,22 @@ class TestInitialization:
 
         assert qml.equal(H([1.2, 2.3], 3.4), expected_H([1.2, 2.3], 3.4))
 
+    def test_initialization_via_addition(self):
+        """Test that adding combinations of operators and numbers/callables initializes
+        a ParametrizedHamiltonian"""
+
+        XX = qml.PauliX(0) @ qml.PauliX(1)
+        YY = qml.PauliY(0) @ qml.PauliY(1)
+        ZZ = qml.PauliZ(0) @ qml.PauliZ(1)
+
+        H = 2 * XX + f1 * YY + f2 * ZZ
+
+        coeffs = [2, f1, f2]
+        ops = [XX, YY, ZZ]
+        expected_H = ParametrizedHamiltonian(coeffs, ops)
+
+        assert qml.equal(H([1.2, 2.3], 3.4), expected_H([1.2, 2.3], 3.4))
+
     def test_mismatched_coeffs_and_obs_raises_error(self):
         """Test that an error is raised if the length of the list of coefficients
         and the list of observables don't match"""
@@ -221,6 +237,7 @@ class TestInteractionWithOperators:
         (qml.Hamiltonian([2], [qml.PauliZ(0)]), 2),
         (qml.Hamiltonian([1.7], [qml.PauliZ(0)]), 1.7),
         (qml.ops.SProd(3, qml.PauliZ(0)), 3),
+        (2 * qml.PauliZ(0), 2),
     )
     ops = (
         qml.PauliX(2),