We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
CV gradients - Heisenberg derivatives
Hey everyone. Maybe we can move some of the discussions out of the paper and to here for now? At least as far as writing equations allows...

Christian, your comment on passing the derivative past a non-Gaussian circuit in the Heisenberg and "circuit" picture is very useful. I have not even thought about the case of U being non-Gaussian and something else than homodyne detection, I was more worried about and \hat{O} = \hat{x} and V being non-Gaussian. Would be interested to know if the same thing appears.

This is a really cool lesson in thorough Heisenberg calculus!

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pennylane/transforms/hamiltonian_expand.py`
Content:
```
1 # Copyright 2018-2021 Xanadu Quantum Technologies Inc.
2 
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """
15 Contains the hamiltonian expand tape transform
16 """
17 # pylint: disable=protected-access
18 import pennylane as qml
19 
20 
21 def hamiltonian_expand(tape, group=True):
22     r"""
23     Splits a tape measuring a Hamiltonian expectation into mutliple tapes of Pauli expectations,
24     and provides a function to recombine the results.
25 
26     Args:
27         tape (.QuantumTape): the tape used when calculating the expectation value
28             of the Hamiltonian
29         group (bool): Whether to compute disjoint groups of commuting Pauli observables, leading to fewer tapes.
30             If grouping information can be found in the Hamiltonian, it will be used even if group=False.
31 
32     Returns:
33         tuple[list[.QuantumTape], function]: Returns a tuple containing a list of
34         quantum tapes to be evaluated, and a function to be applied to these
35         tape executions to compute the expectation value.
36 
37     **Example**
38 
39     Given a Hamiltonian,
40 
41     .. code-block:: python3
42 
43         H = qml.PauliY(2) @ qml.PauliZ(1) + 0.5 * qml.PauliZ(2) + qml.PauliZ(1)
44 
45     and a tape of the form,
46 
47     .. code-block:: python3
48 
49         with qml.tape.QuantumTape() as tape:
50             qml.Hadamard(wires=0)
51             qml.CNOT(wires=[0, 1])
52             qml.PauliX(wires=2)
53 
54             qml.expval(H)
55 
56     We can use the ``hamiltonian_expand`` transform to generate new tapes and a classical
57     post-processing function for computing the expectation value of the Hamiltonian.
58 
59     >>> tapes, fn = qml.transforms.hamiltonian_expand(tape)
60 
61     We can evaluate these tapes on a device:
62 
63     >>> dev = qml.device("default.qubit", wires=3)
64     >>> res = dev.batch_execute(tapes)
65 
66     Applying the processing function results in the expectation value of the Hamiltonian:
67 
68     >>> fn(res)
69     -0.5
70 
71     Fewer tapes can be constructed by grouping commuting observables. This can be achieved
72     by the ``group`` keyword argument:
73 
74     .. code-block:: python3
75 
76         H = qml.Hamiltonian([1., 2., 3.], [qml.PauliZ(0), qml.PauliX(1), qml.PauliX(0)])
77 
78         with qml.tape.QuantumTape() as tape:
79             qml.Hadamard(wires=0)
80             qml.CNOT(wires=[0, 1])
81             qml.PauliX(wires=2)
82             qml.expval(H)
83 
84     With grouping, the Hamiltonian gets split into two groups of observables (here ``[qml.PauliZ(0)]`` and
85     ``[qml.PauliX(1), qml.PauliX(0)]``):
86 
87     >>> tapes, fn = qml.transforms.hamiltonian_expand(tape)
88     >>> len(tapes)
89     2
90 
91     Without grouping it gets split into three groups (``[qml.PauliZ(0)]``, ``[qml.PauliX(1)]`` and ``[qml.PauliX(0)]``):
92 
93     >>> tapes, fn = qml.transforms.hamiltonian_expand(tape, group=False)
94     >>> len(tapes)
95     3
96 
97     Alternatively, if the Hamiltonian has already computed groups, they are used even if ``group=False``:
98 
99     .. code-block:: python3
100 
101         obs = [qml.PauliZ(0), qml.PauliX(1), qml.PauliX(0)]
102         coeffs = [1., 2., 3.]
103         H = qml.Hamiltonian(coeffs, obs, grouping_type='qwc')
104 
105         # the initialisation already computes grouping information and stores it in the Hamiltonian
106         assert H.grouping_indices is not None
107 
108         with qml.tape.QuantumTape() as tape:
109             qml.Hadamard(wires=0)
110             qml.CNOT(wires=[0, 1])
111             qml.PauliX(wires=2)
112             qml.expval(H)
113 
114     Grouping information has been used to reduce the number of tapes from 3 to 2:
115 
116     >>> tapes, fn = qml.transforms.hamiltonian_expand(tape, group=False)
117     >>> len(tapes)
118     2
119     """
120 
121     hamiltonian = tape.measurements[0].obs
122 
123     if (
124         not isinstance(hamiltonian, qml.Hamiltonian)
125         or len(tape.measurements) > 1
126         or tape.measurements[0].return_type != qml.measurements.Expectation
127     ):
128         raise ValueError(
129             "Passed tape must end in `qml.expval(H)`, where H is of type `qml.Hamiltonian`"
130         )
131 
132     # note: for backward passes of some frameworks
133     # it is crucial to use the hamiltonian.data attribute,
134     # and not hamiltonian.coeffs when recombining the results
135 
136     if group or hamiltonian.grouping_indices is not None:
137 
138         if hamiltonian.grouping_indices is None:
139             # explicitly selected grouping, but indices not yet computed
140             hamiltonian.compute_grouping()
141 
142         coeff_groupings = [
143             qml.math.stack([hamiltonian.data[i] for i in indices])
144             for indices in hamiltonian.grouping_indices
145         ]
146         obs_groupings = [
147             [hamiltonian.ops[i] for i in indices] for indices in hamiltonian.grouping_indices
148         ]
149 
150         # make one tape per grouping, measuring the
151         # observables in that grouping
152         tapes = []
153         for obs in obs_groupings:
154             new_tape = tape.__class__(tape._ops, (qml.expval(o) for o in obs), tape._prep)
155 
156             new_tape = new_tape.expand(stop_at=lambda obj: True)
157             tapes.append(new_tape)
158 
159         def processing_fn(res_groupings):
160             dot_products = [
161                 qml.math.dot(r_group, c_group)
162                 for c_group, r_group in zip(coeff_groupings, res_groupings)
163             ]
164             return qml.math.sum(qml.math.stack(dot_products), axis=0)
165 
166         return tapes, processing_fn
167 
168     coeffs = hamiltonian.data
169 
170     # make one tape per observable
171     tapes = []
172     for o in hamiltonian.ops:
173         # pylint: disable=protected-access
174         new_tape = tape.__class__(tape._ops, [qml.expval(o)], tape._prep)
175         tapes.append(new_tape)
176 
177     # pylint: disable=function-redefined
178     def processing_fn(res):
179         dot_products = [qml.math.dot(qml.math.squeeze(r), c) for c, r in zip(coeffs, res)]
180         return qml.math.sum(qml.math.stack(dot_products), axis=0)
181 
182     return tapes, processing_fn
183
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pennylane/transforms/hamiltonian_expand.py b/pennylane/transforms/hamiltonian_expand.py
--- a/pennylane/transforms/hamiltonian_expand.py
+++ b/pennylane/transforms/hamiltonian_expand.py
@@ -157,10 +157,21 @@
             tapes.append(new_tape)
 
         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)
-            ]
+            if qml.active_return():
+                dot_products = [
+                    qml.math.dot(
+                        qml.math.reshape(
+                            qml.math.convert_like(r_group, c_group), qml.math.shape(c_group)
+                        ),
+                        c_group,
+                    )
+                    for c_group, r_group in zip(coeff_groupings, res_groupings)
+                ]
+            else:
+                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)
 
         return tapes, processing_fn

{"golden_diff": "diff --git a/pennylane/transforms/hamiltonian_expand.py b/pennylane/transforms/hamiltonian_expand.py\n--- a/pennylane/transforms/hamiltonian_expand.py\n+++ b/pennylane/transforms/hamiltonian_expand.py\n@@ -157,10 +157,21 @@\n             tapes.append(new_tape)\n \n         def processing_fn(res_groupings):\n-            dot_products = [\n-                qml.math.dot(r_group, c_group)\n-                for c_group, r_group in zip(coeff_groupings, res_groupings)\n-            ]\n+            if qml.active_return():\n+                dot_products = [\n+                    qml.math.dot(\n+                        qml.math.reshape(\n+                            qml.math.convert_like(r_group, c_group), qml.math.shape(c_group)\n+                        ),\n+                        c_group,\n+                    )\n+                    for c_group, r_group in zip(coeff_groupings, res_groupings)\n+                ]\n+            else:\n+                dot_products = [\n+                    qml.math.dot(r_group, c_group)\n+                    for c_group, r_group in zip(coeff_groupings, res_groupings)\n+                ]\n             return qml.math.sum(qml.math.stack(dot_products), axis=0)\n \n         return tapes, processing_fn\n", "issue": "CV gradients - Heisenberg derivatives\nHey everyone. Maybe we can move some of the discussions out of the paper and to here for now? At least as far as writing equations allows...\r\n\r\nChristian, your comment on passing the derivative past a non-Gaussian circuit in the Heisenberg and \"circuit\" picture is very useful. I have not even thought about the case of U being non-Gaussian and something else than homodyne detection, I was more worried about and \\hat{O} = \\hat{x} and V being non-Gaussian. Would be interested to know if the same thing appears.\r\n\r\nThis is a really cool lesson in thorough Heisenberg calculus!\n", "before_files": [{"content": "# Copyright 2018-2021 Xanadu Quantum Technologies Inc.\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n\n#     http://www.apache.org/licenses/LICENSE-2.0\n\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"\nContains the hamiltonian expand tape transform\n\"\"\"\n# pylint: disable=protected-access\nimport pennylane as qml\n\n\ndef hamiltonian_expand(tape, group=True):\n    r\"\"\"\n    Splits a tape measuring a Hamiltonian expectation into mutliple tapes of Pauli expectations,\n    and provides a function to recombine the results.\n\n    Args:\n        tape (.QuantumTape): the tape used when calculating the expectation value\n            of the Hamiltonian\n        group (bool): Whether to compute disjoint groups of commuting Pauli observables, leading to fewer tapes.\n            If grouping information can be found in the Hamiltonian, it will be used even if group=False.\n\n    Returns:\n        tuple[list[.QuantumTape], function]: Returns a tuple containing a list of\n        quantum tapes to be evaluated, and a function to be applied to these\n        tape executions to compute the expectation value.\n\n    **Example**\n\n    Given a Hamiltonian,\n\n    .. code-block:: python3\n\n        H = qml.PauliY(2) @ qml.PauliZ(1) + 0.5 * qml.PauliZ(2) + qml.PauliZ(1)\n\n    and a tape of the form,\n\n    .. code-block:: python3\n\n        with qml.tape.QuantumTape() as tape:\n            qml.Hadamard(wires=0)\n            qml.CNOT(wires=[0, 1])\n            qml.PauliX(wires=2)\n\n            qml.expval(H)\n\n    We can use the ``hamiltonian_expand`` transform to generate new tapes and a classical\n    post-processing function for computing the expectation value of the Hamiltonian.\n\n    >>> tapes, fn = qml.transforms.hamiltonian_expand(tape)\n\n    We can evaluate these tapes on a device:\n\n    >>> dev = qml.device(\"default.qubit\", wires=3)\n    >>> res = dev.batch_execute(tapes)\n\n    Applying the processing function results in the expectation value of the Hamiltonian:\n\n    >>> fn(res)\n    -0.5\n\n    Fewer tapes can be constructed by grouping commuting observables. This can be achieved\n    by the ``group`` keyword argument:\n\n    .. code-block:: python3\n\n        H = qml.Hamiltonian([1., 2., 3.], [qml.PauliZ(0), qml.PauliX(1), qml.PauliX(0)])\n\n        with qml.tape.QuantumTape() as tape:\n            qml.Hadamard(wires=0)\n            qml.CNOT(wires=[0, 1])\n            qml.PauliX(wires=2)\n            qml.expval(H)\n\n    With grouping, the Hamiltonian gets split into two groups of observables (here ``[qml.PauliZ(0)]`` and\n    ``[qml.PauliX(1), qml.PauliX(0)]``):\n\n    >>> tapes, fn = qml.transforms.hamiltonian_expand(tape)\n    >>> len(tapes)\n    2\n\n    Without grouping it gets split into three groups (``[qml.PauliZ(0)]``, ``[qml.PauliX(1)]`` and ``[qml.PauliX(0)]``):\n\n    >>> tapes, fn = qml.transforms.hamiltonian_expand(tape, group=False)\n    >>> len(tapes)\n    3\n\n    Alternatively, if the Hamiltonian has already computed groups, they are used even if ``group=False``:\n\n    .. code-block:: python3\n\n        obs = [qml.PauliZ(0), qml.PauliX(1), qml.PauliX(0)]\n        coeffs = [1., 2., 3.]\n        H = qml.Hamiltonian(coeffs, obs, grouping_type='qwc')\n\n        # the initialisation already computes grouping information and stores it in the Hamiltonian\n        assert H.grouping_indices is not None\n\n        with qml.tape.QuantumTape() as tape:\n            qml.Hadamard(wires=0)\n            qml.CNOT(wires=[0, 1])\n            qml.PauliX(wires=2)\n            qml.expval(H)\n\n    Grouping information has been used to reduce the number of tapes from 3 to 2:\n\n    >>> tapes, fn = qml.transforms.hamiltonian_expand(tape, group=False)\n    >>> len(tapes)\n    2\n    \"\"\"\n\n    hamiltonian = tape.measurements[0].obs\n\n    if (\n        not isinstance(hamiltonian, qml.Hamiltonian)\n        or len(tape.measurements) > 1\n        or tape.measurements[0].return_type != qml.measurements.Expectation\n    ):\n        raise ValueError(\n            \"Passed tape must end in `qml.expval(H)`, where H is of type `qml.Hamiltonian`\"\n        )\n\n    # note: for backward passes of some frameworks\n    # it is crucial to use the hamiltonian.data attribute,\n    # and not hamiltonian.coeffs when recombining the results\n\n    if group or hamiltonian.grouping_indices is not None:\n\n        if hamiltonian.grouping_indices is None:\n            # explicitly selected grouping, but indices not yet computed\n            hamiltonian.compute_grouping()\n\n        coeff_groupings = [\n            qml.math.stack([hamiltonian.data[i] for i in indices])\n            for indices in hamiltonian.grouping_indices\n        ]\n        obs_groupings = [\n            [hamiltonian.ops[i] for i in indices] for indices in hamiltonian.grouping_indices\n        ]\n\n        # make one tape per grouping, measuring the\n        # observables in that grouping\n        tapes = []\n        for obs in obs_groupings:\n            new_tape = tape.__class__(tape._ops, (qml.expval(o) for o in obs), tape._prep)\n\n            new_tape = new_tape.expand(stop_at=lambda obj: True)\n            tapes.append(new_tape)\n\n        def processing_fn(res_groupings):\n            dot_products = [\n                qml.math.dot(r_group, c_group)\n                for c_group, r_group in zip(coeff_groupings, res_groupings)\n            ]\n            return qml.math.sum(qml.math.stack(dot_products), axis=0)\n\n        return tapes, processing_fn\n\n    coeffs = hamiltonian.data\n\n    # make one tape per observable\n    tapes = []\n    for o in hamiltonian.ops:\n        # pylint: disable=protected-access\n        new_tape = tape.__class__(tape._ops, [qml.expval(o)], tape._prep)\n        tapes.append(new_tape)\n\n    # pylint: disable=function-redefined\n    def processing_fn(res):\n        dot_products = [qml.math.dot(qml.math.squeeze(r), c) for c, r in zip(coeffs, res)]\n        return qml.math.sum(qml.math.stack(dot_products), axis=0)\n\n    return tapes, processing_fn\n", "path": "pennylane/transforms/hamiltonian_expand.py"}], "after_files": [{"content": "# Copyright 2018-2021 Xanadu Quantum Technologies Inc.\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n\n#     http://www.apache.org/licenses/LICENSE-2.0\n\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"\nContains the hamiltonian expand tape transform\n\"\"\"\n# pylint: disable=protected-access\nimport pennylane as qml\n\n\ndef hamiltonian_expand(tape, group=True):\n    r\"\"\"\n    Splits a tape measuring a Hamiltonian expectation into mutliple tapes of Pauli expectations,\n    and provides a function to recombine the results.\n\n    Args:\n        tape (.QuantumTape): the tape used when calculating the expectation value\n            of the Hamiltonian\n        group (bool): Whether to compute disjoint groups of commuting Pauli observables, leading to fewer tapes.\n            If grouping information can be found in the Hamiltonian, it will be used even if group=False.\n\n    Returns:\n        tuple[list[.QuantumTape], function]: Returns a tuple containing a list of\n        quantum tapes to be evaluated, and a function to be applied to these\n        tape executions to compute the expectation value.\n\n    **Example**\n\n    Given a Hamiltonian,\n\n    .. code-block:: python3\n\n        H = qml.PauliY(2) @ qml.PauliZ(1) + 0.5 * qml.PauliZ(2) + qml.PauliZ(1)\n\n    and a tape of the form,\n\n    .. code-block:: python3\n\n        with qml.tape.QuantumTape() as tape:\n            qml.Hadamard(wires=0)\n            qml.CNOT(wires=[0, 1])\n            qml.PauliX(wires=2)\n\n            qml.expval(H)\n\n    We can use the ``hamiltonian_expand`` transform to generate new tapes and a classical\n    post-processing function for computing the expectation value of the Hamiltonian.\n\n    >>> tapes, fn = qml.transforms.hamiltonian_expand(tape)\n\n    We can evaluate these tapes on a device:\n\n    >>> dev = qml.device(\"default.qubit\", wires=3)\n    >>> res = dev.batch_execute(tapes)\n\n    Applying the processing function results in the expectation value of the Hamiltonian:\n\n    >>> fn(res)\n    -0.5\n\n    Fewer tapes can be constructed by grouping commuting observables. This can be achieved\n    by the ``group`` keyword argument:\n\n    .. code-block:: python3\n\n        H = qml.Hamiltonian([1., 2., 3.], [qml.PauliZ(0), qml.PauliX(1), qml.PauliX(0)])\n\n        with qml.tape.QuantumTape() as tape:\n            qml.Hadamard(wires=0)\n            qml.CNOT(wires=[0, 1])\n            qml.PauliX(wires=2)\n            qml.expval(H)\n\n    With grouping, the Hamiltonian gets split into two groups of observables (here ``[qml.PauliZ(0)]`` and\n    ``[qml.PauliX(1), qml.PauliX(0)]``):\n\n    >>> tapes, fn = qml.transforms.hamiltonian_expand(tape)\n    >>> len(tapes)\n    2\n\n    Without grouping it gets split into three groups (``[qml.PauliZ(0)]``, ``[qml.PauliX(1)]`` and ``[qml.PauliX(0)]``):\n\n    >>> tapes, fn = qml.transforms.hamiltonian_expand(tape, group=False)\n    >>> len(tapes)\n    3\n\n    Alternatively, if the Hamiltonian has already computed groups, they are used even if ``group=False``:\n\n    .. code-block:: python3\n\n        obs = [qml.PauliZ(0), qml.PauliX(1), qml.PauliX(0)]\n        coeffs = [1., 2., 3.]\n        H = qml.Hamiltonian(coeffs, obs, grouping_type='qwc')\n\n        # the initialisation already computes grouping information and stores it in the Hamiltonian\n        assert H.grouping_indices is not None\n\n        with qml.tape.QuantumTape() as tape:\n            qml.Hadamard(wires=0)\n            qml.CNOT(wires=[0, 1])\n            qml.PauliX(wires=2)\n            qml.expval(H)\n\n    Grouping information has been used to reduce the number of tapes from 3 to 2:\n\n    >>> tapes, fn = qml.transforms.hamiltonian_expand(tape, group=False)\n    >>> len(tapes)\n    2\n    \"\"\"\n\n    hamiltonian = tape.measurements[0].obs\n\n    if (\n        not isinstance(hamiltonian, qml.Hamiltonian)\n        or len(tape.measurements) > 1\n        or tape.measurements[0].return_type != qml.measurements.Expectation\n    ):\n        raise ValueError(\n            \"Passed tape must end in `qml.expval(H)`, where H is of type `qml.Hamiltonian`\"\n        )\n\n    # note: for backward passes of some frameworks\n    # it is crucial to use the hamiltonian.data attribute,\n    # and not hamiltonian.coeffs when recombining the results\n\n    if group or hamiltonian.grouping_indices is not None:\n\n        if hamiltonian.grouping_indices is None:\n            # explicitly selected grouping, but indices not yet computed\n            hamiltonian.compute_grouping()\n\n        coeff_groupings = [\n            qml.math.stack([hamiltonian.data[i] for i in indices])\n            for indices in hamiltonian.grouping_indices\n        ]\n        obs_groupings = [\n            [hamiltonian.ops[i] for i in indices] for indices in hamiltonian.grouping_indices\n        ]\n\n        # make one tape per grouping, measuring the\n        # observables in that grouping\n        tapes = []\n        for obs in obs_groupings:\n            new_tape = tape.__class__(tape._ops, (qml.expval(o) for o in obs), tape._prep)\n\n            new_tape = new_tape.expand(stop_at=lambda obj: True)\n            tapes.append(new_tape)\n\n        def processing_fn(res_groupings):\n            if qml.active_return():\n                dot_products = [\n                    qml.math.dot(\n                        qml.math.reshape(\n                            qml.math.convert_like(r_group, c_group), qml.math.shape(c_group)\n                        ),\n                        c_group,\n                    )\n                    for c_group, r_group in zip(coeff_groupings, res_groupings)\n                ]\n            else:\n                dot_products = [\n                    qml.math.dot(r_group, c_group)\n                    for c_group, r_group in zip(coeff_groupings, res_groupings)\n                ]\n            return qml.math.sum(qml.math.stack(dot_products), axis=0)\n\n        return tapes, processing_fn\n\n    coeffs = hamiltonian.data\n\n    # make one tape per observable\n    tapes = []\n    for o in hamiltonian.ops:\n        # pylint: disable=protected-access\n        new_tape = tape.__class__(tape._ops, [qml.expval(o)], tape._prep)\n        tapes.append(new_tape)\n\n    # pylint: disable=function-redefined\n    def processing_fn(res):\n        dot_products = [qml.math.dot(qml.math.squeeze(r), c) for c, r in zip(coeffs, res)]\n        return qml.math.sum(qml.math.stack(dot_products), axis=0)\n\n    return tapes, processing_fn\n", "path": "pennylane/transforms/hamiltonian_expand.py"}]}