Repository: PennyLaneAI/pennylane
Pull request #1586 (2021-08-24 09:05:56)

Fix VQE issue with tensorflow
**Context:**

When implementing the new VQE workflow, I overlooked that the tensorflow differentiation tests were skipped due to a typo. It turns out that they were failing, which uncovered a subtle issue.

**Description of the Change:**

Uses `hamiltonian.data` instead of `hamiltonian.coeffs` in the `hamiltonian_expand` function. 

Rewrites the function to treat grouping and non-grouping entirely separate, since they produce tensors of different dimensions.

**Benefits:**

Hopefully this fixes the entire problem.

**Possible Drawbacks:**

None

**Related GitHub Issues:**


github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [.github/CHANGELOG.md](/PennyLaneAI/pennylane/blob/master/.github/CHANGELOG.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/1586?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#1586](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1586?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (0429adb) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/4c09cda4e5105aebf8ec089ed4c3af8af8b1c543?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (4c09cda) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1586/graphs/tree.svg?width=650&height=150&src=pr&token=9S3YzlUgGk&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1586?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

```diff
@@           Coverage Diff           @@
##           master    #1586   +/-   ##
=======================================
  Coverage   99.16%   99.16%           
=======================================
  Files         189      189           
  Lines       13642    13648    +6     
=======================================
+ Hits        13528    13534    +6     
  Misses        114      114           
```


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

------

[Continue to review full report at Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1586?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1586?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Last update [4c09cda...0429adb](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1586?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

mariaschuld: > I'm curious how you caught the typo!

I just looked properly on the test results and saw the skips...
josh146: > I just looked properly on the test results and saw the skips...

Oh, I'm not sure I would have noticed 😆 I think I'm too trusting of PyTest

Patch:
diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index fc0a62ece..6208f72a2 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -252,6 +252,9 @@ and requirements-ci.txt (unpinned). This latter would be used by the CI.
 
 <h3>Bug fixes</h3>
 
+* Fix bug when computing expectations of Hamiltonians using TensorFlow.
+  [(#1586)](https://github.com/PennyLaneAI/pennylane/pull/1586)
+
 * Fix bug when computing the specs of a circuit with a Hamiltonian observable.
   [(#1533)](https://github.com/PennyLaneAI/pennylane/pull/1533)
 
diff --git a/pennylane/transforms/hamiltonian_expand.py b/pennylane/transforms/hamiltonian_expand.py
index 0bd9104a8..127fc9789 100644
--- a/pennylane/transforms/hamiltonian_expand.py
+++ b/pennylane/transforms/hamiltonian_expand.py
@@ -129,20 +129,26 @@ def hamiltonian_expand(tape, group=True):
             "Passed tape must end in `qml.expval(H)`, where H is of type `qml.Hamiltonian`"
         )
 
+    # note: for backward passes of some frameworks
+    # it is crucial to use the hamiltonian.data attribute,
+    # and not hamiltonian.coeffs when recombining the results
+
     if group or hamiltonian.grouping_indices is not None:
 
         if hamiltonian.grouping_indices is None:
+            # explicitly selected grouping, but indices not yet computed
             hamiltonian.compute_grouping()
 
-        # use groups of observables if available or explicitly requested
-        coeffs = [
-            qml.math.squeeze(qml.math.take(hamiltonian.coeffs, indices, axis=0))
+        coeff_groupings = [
+            qml.math.stack([hamiltonian.data[i] for i in indices])
             for indices in hamiltonian.grouping_indices
         ]
         obs_groupings = [
             [hamiltonian.ops[i] for i in indices] for indices in hamiltonian.grouping_indices
         ]
 
+        # make one tape per grouping, measuring the
+        # observables in that grouping
         tapes = []
         for obs in obs_groupings:
 
@@ -155,21 +161,30 @@ def hamiltonian_expand(tape, group=True):
 
             new_tape = new_tape.expand(stop_at=lambda obj: True)
             tapes.append(new_tape)
-    else:
-        coeffs = hamiltonian.coeffs
 
-        tapes = []
-        for o in hamiltonian.ops:
-            with tape.__class__() as new_tape:
-                for op in tape.operations:
-                    op.queue()
-                qml.expval(o)
+        def processing_fn(res_groupings):
+            dot_products = [
+                qml.math.dot(r_group, c_group)
+                for c_group, r_group in zip(coeff_groupings, res_groupings)
+            ]
+            return qml.math.sum(qml.math.stack(dot_products), axis=0)
 
-            tapes.append(new_tape)
+        return tapes, processing_fn
+
+    coeffs = hamiltonian.data
+
+    # make one tape per observable
+    tapes = []
+    for o in hamiltonian.ops:
+        with tape.__class__() as new_tape:
+            for op in tape.operations:
+                op.queue()
+            qml.expval(o)
+
+        tapes.append(new_tape)
 
+    # pylint: disable=function-redefined
     def processing_fn(res):
-        # note: res could have an extra dimension here if a shots_distribution
-        # is used for evaluation
         dot_products = [qml.math.dot(qml.math.squeeze(r), c) for c, r in zip(coeffs, res)]
         return qml.math.sum(qml.math.stack(dot_products), axis=0)
 


Test patch:
diff --git a/tests/ops/test_hamiltonian.py b/tests/ops/test_hamiltonian.py
index 9c8380aa6..ad277fe2c 100644
--- a/tests/ops/test_hamiltonian.py
+++ b/tests/ops/test_hamiltonian.py
@@ -1376,7 +1376,7 @@ class TestHamiltonianDifferentiation:
         """Test the tf interface by comparing the differentiation of linearly combined subcircuits
         with the differentiation of a Hamiltonian expectation"""
 
-        tf = pytest.importorskip("tf")
+        tf = pytest.importorskip("tensorflow")
         coeffs = tf.Variable([-0.05, 0.17], dtype=tf.double)
         param = tf.Variable(1.7, dtype=tf.double)
 
diff --git a/tests/transforms/test_hamiltonian_expand.py b/tests/transforms/test_hamiltonian_expand.py
index cbfcd758b..a01026b00 100644
--- a/tests/transforms/test_hamiltonian_expand.py
+++ b/tests/transforms/test_hamiltonian_expand.py
@@ -139,7 +139,7 @@ class TestHamiltonianExpval:
         with pytest.raises(ValueError, match=r"Passed tape must end in"):
             tapes, fn = qml.transforms.hamiltonian_expand(tape)
 
-    def test_hamiltonian_dif_autograd(self):
+    def test_hamiltonian_dif_autograd(self, tol):
         """Tests that the hamiltonian_expand tape transform is differentiable with the Autograd interface"""
 
         H = qml.Hamiltonian(
@@ -153,9 +153,9 @@ class TestHamiltonianExpval:
             np.array(0.4),
             np.array(-0.5),
             np.array(0.7),
-            np.array(0.4),
-            np.array(-0.5),
-            np.array(0.7),
+            np.array(-0.2),
+            np.array(0.5),
+            np.array(1.0),
         ]
         output = 0.42294409781940356
         output2 = [
@@ -165,9 +165,9 @@ class TestHamiltonianExpval:
             -1.94289029e-09,
             3.50307411e-01,
             -3.41123470e-01,
-            0.0,  # these three are the Hamiltonian parameters
-            0.0,
             0.0,
+            -0.43657,
+            0.64123,
         ]
 
         with qml.tape.JacobianTape() as tape:
@@ -192,7 +192,7 @@ class TestHamiltonianExpval:
         assert np.isclose(cost(var), output)
         grad = qml.grad(cost)(var)
         for g, o in zip(grad, output2):
-            assert np.allclose(g, o)
+            assert np.allclose(g, o, atol=tol)
 
     def test_hamiltonian_dif_tensorflow(self):
         """Tests that the hamiltonian_expand tape transform is differentiable with the Tensorflow interface"""