We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Hamiltonians created via addition produce incorrect results with `ExpvalCost(optimize=True)`
Good afternoon, 
I was having problem with vqe when I set the flag: optmize = True . I noticed that there were errors when entering Hamiltonians of the form Z1@Z2 + Z2@Z1. We know it is the same as 2.Z1.Z2 but the optimizer seems to have some kind of problem with this kind of Hamiltonians (vqe with optimize = False works correctly in this situation).
Although by preprocessing the Hamiltonian we avoid the problem, I was writing to take the problem into account.
Greetings!

--- 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/grouping/group_observables.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 This module contains the high-level Pauli-word-partitioning functionality used in measurement optimization.
16 """
17 
18 from pennylane.wires import Wires
19 from pennylane.grouping.utils import (
20     observables_to_binary_matrix,
21     binary_to_pauli,
22     are_identical_pauli_words,
23     qwc_complement_adj_matrix,
24 )
25 from pennylane.grouping.graph_colouring import largest_first, recursive_largest_first
26 import numpy as np
27 
28 GROUPING_TYPES = frozenset(["qwc", "commuting", "anticommuting"])
29 GRAPH_COLOURING_METHODS = {"lf": largest_first, "rlf": recursive_largest_first}
30 
31 
32 class PauliGroupingStrategy:  # pylint: disable=too-many-instance-attributes
33     """
34     Class for partitioning a list of Pauli words according to some binary symmetric relation.
35 
36     Partitions are defined by the binary symmetric relation of interest, e.g., all Pauli words in a
37     partition being mutually commuting. The partitioning is accomplished by formulating the list of
38     Pauli words as a graph where nodes represent Pauli words and edges between nodes denotes that
39     the two corresponding Pauli words satisfy the symmetric binary relation.
40 
41     Obtaining the fewest number of partitions such that all Pauli terms within a partition mutually
42     satisfy the binary relation can then be accomplished by finding a partition of the graph nodes
43     such that each partition is a fully connected subgraph (a "clique"). The problem of finding the
44     optimal partitioning, i.e., the fewest number of cliques, is the minimum clique cover (MCC)
45     problem. The solution of MCC may be found by graph colouring on the complementary graph. Both
46     MCC and graph colouring are NP-Hard, so heuristic graph colouring algorithms are employed to
47     find approximate solutions in polynomial time.
48 
49     Args:
50         observables (list[Observable]): a list of Pauli words to be partitioned according to a
51             grouping strategy
52         grouping_type (str): the binary relation used to define partitions of
53             the Pauli words, can be ``'qwc'`` (qubit-wise commuting), ``'commuting'``, or
54             ``'anticommuting'``.
55         graph_colourer (str): the heuristic algorithm to employ for graph
56             colouring, can be ``'lf'`` (Largest First) or ``'rlf'`` (Recursive
57             Largest First)
58 
59     Raises:
60         ValueError: if arguments specified for ``grouping_type`` or
61             ``graph_colourer`` are not recognized
62     """
63 
64     def __init__(self, observables, grouping_type="qwc", graph_colourer="rlf"):
65 
66         if grouping_type.lower() not in GROUPING_TYPES:
67             raise ValueError(
68                 "Grouping type must be one of: {}, instead got {}.".format(
69                     GROUPING_TYPES, grouping_type
70                 )
71             )
72 
73         self.grouping_type = grouping_type.lower()
74 
75         if graph_colourer.lower() not in GRAPH_COLOURING_METHODS.keys():
76             raise ValueError(
77                 "Graph colouring method must be one of: {}, instead got {}.".format(
78                     list(GRAPH_COLOURING_METHODS.keys()), graph_colourer
79                 )
80             )
81 
82         self.graph_colourer = GRAPH_COLOURING_METHODS[graph_colourer.lower()]
83         self.observables = observables
84         self._wire_map = None
85         self._n_qubits = None
86         self.binary_observables = None
87         self.adj_matrix = None
88         self.grouped_paulis = None
89 
90     def binary_repr(self, n_qubits=None, wire_map=None):
91         """Converts the list of Pauli words to a binary matrix.
92 
93         Args:
94             n_qubits (int): number of qubits to specify dimension of binary vector representation
95             wire_map (dict): dictionary containing all wire labels used in the Pauli word as keys,
96                 and unique integer labels as their values
97 
98         Returns:
99             array[int]: a column matrix of the Pauli words in binary vector representation
100         """
101 
102         if wire_map is None:
103             self._wire_map = {
104                 wire: c
105                 for c, wire in enumerate(
106                     Wires.all_wires([obs.wires for obs in self.observables]).tolist()
107                 )
108             }
109 
110         else:
111             self._wire_map = wire_map
112 
113         self._n_qubits = n_qubits
114 
115         return observables_to_binary_matrix(self.observables, n_qubits, self._wire_map)
116 
117     def complement_adj_matrix_for_operator(self):
118         """Constructs the adjacency matrix for the complement of the Pauli graph.
119 
120         The adjacency matrix for an undirected graph of N vertices is an N by N symmetric binary
121         matrix, where matrix elements of 1 denote an edge, and matrix elements of 0 denote no edge.
122 
123         Returns:
124             array[int]: the square and symmetric adjacency matrix
125         """
126 
127         if self.binary_observables is None:
128             self.binary_observables = self.binary_repr()
129 
130         n_qubits = int(np.shape(self.binary_observables)[1] / 2)
131 
132         if self.grouping_type == "qwc":
133             adj = qwc_complement_adj_matrix(self.binary_observables)
134 
135         elif self.grouping_type in frozenset(["commuting", "anticommuting"]):
136             symplectic_form = np.block(
137                 [
138                     [np.zeros((n_qubits, n_qubits)), np.eye(n_qubits)],
139                     [np.eye(n_qubits), np.zeros((n_qubits, n_qubits))],
140                 ]
141             )
142             mat_prod = (
143                 self.binary_observables @ symplectic_form @ np.transpose(self.binary_observables)
144             )
145 
146             if self.grouping_type == "commuting":
147 
148                 adj = mat_prod % 2
149 
150             elif self.grouping_type == "anticommuting":
151 
152                 adj = (mat_prod + 1) % 2
153                 np.fill_diagonal(adj, 0)
154 
155         return adj
156 
157     def colour_pauli_graph(self):
158         """
159         Runs the graph colouring heuristic algorithm to obtain the partitioned Pauli words.
160 
161         Returns:
162             list[list[Observable]]: a list of the obtained groupings. Each grouping is itself a
163             list of Pauli word ``Observable`` instances
164         """
165 
166         if self.adj_matrix is None:
167             self.adj_matrix = self.complement_adj_matrix_for_operator()
168 
169         coloured_binary_paulis = self.graph_colourer(self.binary_observables, self.adj_matrix)
170 
171         self.grouped_paulis = [
172             [binary_to_pauli(pauli_word, wire_map=self._wire_map) for pauli_word in grouping]
173             for grouping in coloured_binary_paulis.values()
174         ]
175 
176         return self.grouped_paulis
177 
178 
179 def group_observables(observables, coefficients=None, grouping_type="qwc", method="rlf"):
180     """Partitions a list of observables (Pauli operations and tensor products thereof) into
181     groupings according to a binary relation (qubit-wise commuting, fully-commuting, or
182     anticommuting).
183 
184     Partitions are found by 1) mapping the list of observables to a graph where vertices represent
185     observables and edges encode the binary relation, then 2) solving minimum clique cover for the
186     graph using graph-colouring heuristic algorithms.
187 
188     Args:
189         observables (list[Observable]): a list of Pauli word ``Observable`` instances (Pauli
190             operation instances and :class:`~.Tensor` instances thereof)
191         coefficients (list[float]): A list of float coefficients. If not specified,
192             output ``partitioned_coeffs`` is not returned.
193         grouping_type (str): The type of binary relation between Pauli words.
194             Can be ``'qwc'``, ``'commuting'``, or ``'anticommuting'``.
195         method (str): the graph coloring heuristic to use in solving minimum clique cover, which
196             can be ``'lf'`` (Largest First) or ``'rlf'`` (Recursive Largest First)
197 
198     Returns:
199        tuple:
200 
201            * list[list[Observable]]: A list of the obtained groupings. Each grouping
202              is itself a list of Pauli word ``Observable`` instances.
203            * list[list[float]]: A list of coefficient groupings. Each coefficient
204              grouping is itself a list of the grouping's corresponding coefficients. This is only
205              output if coefficients are specified.
206 
207     Raises:
208         IndexError: if the input list of coefficients is not of the same length as the input list
209             of Pauli words
210 
211     **Example**
212 
213     >>> obs = [qml.PauliY(0), qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(1)]
214     >>> coeffs = [1.43, 4.21, 0.97]
215     >>> obs_groupings, coeffs_groupings = group_observables(obs, coeffs, 'anticommuting', 'lf')
216     >>> obs_groupings
217     [[PauliZ(wires=[1]), PauliX(wires=[0]) @ PauliX(wires=[1])],
218      [PauliY(wires=[0])]]
219     >>> coeffs_groupings
220     [[0.97, 4.21], [1.43]]
221     """
222 
223     if coefficients is not None:
224         if len(coefficients) != len(observables):
225             raise IndexError(
226                 "The coefficients list must be the same length as the observables list."
227             )
228 
229     pauli_grouping = PauliGroupingStrategy(
230         observables, grouping_type=grouping_type, graph_colourer=method
231     )
232     partitioned_paulis = pauli_grouping.colour_pauli_graph()
233 
234     if coefficients is None:
235         return partitioned_paulis
236 
237     partitioned_coeffs = [[0] * len(g) for g in partitioned_paulis]
238 
239     for i, partition in enumerate(partitioned_paulis):
240         for j, pauli_word in enumerate(partition):
241             for observable in observables:
242                 if are_identical_pauli_words(pauli_word, observable):
243                     partitioned_coeffs[i][j] = coefficients[observables.index(observable)]
244                     break
245 
246     return partitioned_paulis, partitioned_coeffs
247
```
--- 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/grouping/group_observables.py b/pennylane/grouping/group_observables.py
--- a/pennylane/grouping/group_observables.py
+++ b/pennylane/grouping/group_observables.py
@@ -15,15 +15,18 @@
 This module contains the high-level Pauli-word-partitioning functionality used in measurement optimization.
 """
 
-from pennylane.wires import Wires
+from copy import copy
+
+import numpy as np
+
+from pennylane.grouping.graph_colouring import largest_first, recursive_largest_first
 from pennylane.grouping.utils import (
-    observables_to_binary_matrix,
-    binary_to_pauli,
     are_identical_pauli_words,
+    binary_to_pauli,
+    observables_to_binary_matrix,
     qwc_complement_adj_matrix,
 )
-from pennylane.grouping.graph_colouring import largest_first, recursive_largest_first
-import numpy as np
+from pennylane.wires import Wires
 
 GROUPING_TYPES = frozenset(["qwc", "commuting", "anticommuting"])
 GRAPH_COLOURING_METHODS = {"lf": largest_first, "rlf": recursive_largest_first}
@@ -236,11 +239,16 @@
 
     partitioned_coeffs = [[0] * len(g) for g in partitioned_paulis]
 
+    observables = copy(observables)
+    coefficients = copy(coefficients)
     for i, partition in enumerate(partitioned_paulis):
         for j, pauli_word in enumerate(partition):
             for observable in observables:
                 if are_identical_pauli_words(pauli_word, observable):
-                    partitioned_coeffs[i][j] = coefficients[observables.index(observable)]
+                    ind = observables.index(observable)
+                    partitioned_coeffs[i][j] = coefficients[ind]
+                    observables.pop(ind)
+                    coefficients.pop(ind)
                     break
 
     return partitioned_paulis, partitioned_coeffs

{"golden_diff": "diff --git a/pennylane/grouping/group_observables.py b/pennylane/grouping/group_observables.py\n--- a/pennylane/grouping/group_observables.py\n+++ b/pennylane/grouping/group_observables.py\n@@ -15,15 +15,18 @@\n This module contains the high-level Pauli-word-partitioning functionality used in measurement optimization.\n \"\"\"\n \n-from pennylane.wires import Wires\n+from copy import copy\n+\n+import numpy as np\n+\n+from pennylane.grouping.graph_colouring import largest_first, recursive_largest_first\n from pennylane.grouping.utils import (\n-    observables_to_binary_matrix,\n-    binary_to_pauli,\n     are_identical_pauli_words,\n+    binary_to_pauli,\n+    observables_to_binary_matrix,\n     qwc_complement_adj_matrix,\n )\n-from pennylane.grouping.graph_colouring import largest_first, recursive_largest_first\n-import numpy as np\n+from pennylane.wires import Wires\n \n GROUPING_TYPES = frozenset([\"qwc\", \"commuting\", \"anticommuting\"])\n GRAPH_COLOURING_METHODS = {\"lf\": largest_first, \"rlf\": recursive_largest_first}\n@@ -236,11 +239,16 @@\n \n     partitioned_coeffs = [[0] * len(g) for g in partitioned_paulis]\n \n+    observables = copy(observables)\n+    coefficients = copy(coefficients)\n     for i, partition in enumerate(partitioned_paulis):\n         for j, pauli_word in enumerate(partition):\n             for observable in observables:\n                 if are_identical_pauli_words(pauli_word, observable):\n-                    partitioned_coeffs[i][j] = coefficients[observables.index(observable)]\n+                    ind = observables.index(observable)\n+                    partitioned_coeffs[i][j] = coefficients[ind]\n+                    observables.pop(ind)\n+                    coefficients.pop(ind)\n                     break\n \n     return partitioned_paulis, partitioned_coeffs\n", "issue": "Hamiltonians created via addition produce incorrect results with `ExpvalCost(optimize=True)`\nGood afternoon, \r\nI was having problem with vqe when I set the flag: optmize = True . I noticed that there were errors when entering Hamiltonians of the form Z1@Z2 + Z2@Z1. We know it is the same as 2.Z1.Z2 but the optimizer seems to have some kind of problem with this kind of Hamiltonians (vqe with optimize = False works correctly in this situation).\r\nAlthough by preprocessing the Hamiltonian we avoid the problem, I was writing to take the problem into account.\r\nGreetings!\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\"\"\"\nThis module contains the high-level Pauli-word-partitioning functionality used in measurement optimization.\n\"\"\"\n\nfrom pennylane.wires import Wires\nfrom pennylane.grouping.utils import (\n    observables_to_binary_matrix,\n    binary_to_pauli,\n    are_identical_pauli_words,\n    qwc_complement_adj_matrix,\n)\nfrom pennylane.grouping.graph_colouring import largest_first, recursive_largest_first\nimport numpy as np\n\nGROUPING_TYPES = frozenset([\"qwc\", \"commuting\", \"anticommuting\"])\nGRAPH_COLOURING_METHODS = {\"lf\": largest_first, \"rlf\": recursive_largest_first}\n\n\nclass PauliGroupingStrategy:  # pylint: disable=too-many-instance-attributes\n    \"\"\"\n    Class for partitioning a list of Pauli words according to some binary symmetric relation.\n\n    Partitions are defined by the binary symmetric relation of interest, e.g., all Pauli words in a\n    partition being mutually commuting. The partitioning is accomplished by formulating the list of\n    Pauli words as a graph where nodes represent Pauli words and edges between nodes denotes that\n    the two corresponding Pauli words satisfy the symmetric binary relation.\n\n    Obtaining the fewest number of partitions such that all Pauli terms within a partition mutually\n    satisfy the binary relation can then be accomplished by finding a partition of the graph nodes\n    such that each partition is a fully connected subgraph (a \"clique\"). The problem of finding the\n    optimal partitioning, i.e., the fewest number of cliques, is the minimum clique cover (MCC)\n    problem. The solution of MCC may be found by graph colouring on the complementary graph. Both\n    MCC and graph colouring are NP-Hard, so heuristic graph colouring algorithms are employed to\n    find approximate solutions in polynomial time.\n\n    Args:\n        observables (list[Observable]): a list of Pauli words to be partitioned according to a\n            grouping strategy\n        grouping_type (str): the binary relation used to define partitions of\n            the Pauli words, can be ``'qwc'`` (qubit-wise commuting), ``'commuting'``, or\n            ``'anticommuting'``.\n        graph_colourer (str): the heuristic algorithm to employ for graph\n            colouring, can be ``'lf'`` (Largest First) or ``'rlf'`` (Recursive\n            Largest First)\n\n    Raises:\n        ValueError: if arguments specified for ``grouping_type`` or\n            ``graph_colourer`` are not recognized\n    \"\"\"\n\n    def __init__(self, observables, grouping_type=\"qwc\", graph_colourer=\"rlf\"):\n\n        if grouping_type.lower() not in GROUPING_TYPES:\n            raise ValueError(\n                \"Grouping type must be one of: {}, instead got {}.\".format(\n                    GROUPING_TYPES, grouping_type\n                )\n            )\n\n        self.grouping_type = grouping_type.lower()\n\n        if graph_colourer.lower() not in GRAPH_COLOURING_METHODS.keys():\n            raise ValueError(\n                \"Graph colouring method must be one of: {}, instead got {}.\".format(\n                    list(GRAPH_COLOURING_METHODS.keys()), graph_colourer\n                )\n            )\n\n        self.graph_colourer = GRAPH_COLOURING_METHODS[graph_colourer.lower()]\n        self.observables = observables\n        self._wire_map = None\n        self._n_qubits = None\n        self.binary_observables = None\n        self.adj_matrix = None\n        self.grouped_paulis = None\n\n    def binary_repr(self, n_qubits=None, wire_map=None):\n        \"\"\"Converts the list of Pauli words to a binary matrix.\n\n        Args:\n            n_qubits (int): number of qubits to specify dimension of binary vector representation\n            wire_map (dict): dictionary containing all wire labels used in the Pauli word as keys,\n                and unique integer labels as their values\n\n        Returns:\n            array[int]: a column matrix of the Pauli words in binary vector representation\n        \"\"\"\n\n        if wire_map is None:\n            self._wire_map = {\n                wire: c\n                for c, wire in enumerate(\n                    Wires.all_wires([obs.wires for obs in self.observables]).tolist()\n                )\n            }\n\n        else:\n            self._wire_map = wire_map\n\n        self._n_qubits = n_qubits\n\n        return observables_to_binary_matrix(self.observables, n_qubits, self._wire_map)\n\n    def complement_adj_matrix_for_operator(self):\n        \"\"\"Constructs the adjacency matrix for the complement of the Pauli graph.\n\n        The adjacency matrix for an undirected graph of N vertices is an N by N symmetric binary\n        matrix, where matrix elements of 1 denote an edge, and matrix elements of 0 denote no edge.\n\n        Returns:\n            array[int]: the square and symmetric adjacency matrix\n        \"\"\"\n\n        if self.binary_observables is None:\n            self.binary_observables = self.binary_repr()\n\n        n_qubits = int(np.shape(self.binary_observables)[1] / 2)\n\n        if self.grouping_type == \"qwc\":\n            adj = qwc_complement_adj_matrix(self.binary_observables)\n\n        elif self.grouping_type in frozenset([\"commuting\", \"anticommuting\"]):\n            symplectic_form = np.block(\n                [\n                    [np.zeros((n_qubits, n_qubits)), np.eye(n_qubits)],\n                    [np.eye(n_qubits), np.zeros((n_qubits, n_qubits))],\n                ]\n            )\n            mat_prod = (\n                self.binary_observables @ symplectic_form @ np.transpose(self.binary_observables)\n            )\n\n            if self.grouping_type == \"commuting\":\n\n                adj = mat_prod % 2\n\n            elif self.grouping_type == \"anticommuting\":\n\n                adj = (mat_prod + 1) % 2\n                np.fill_diagonal(adj, 0)\n\n        return adj\n\n    def colour_pauli_graph(self):\n        \"\"\"\n        Runs the graph colouring heuristic algorithm to obtain the partitioned Pauli words.\n\n        Returns:\n            list[list[Observable]]: a list of the obtained groupings. Each grouping is itself a\n            list of Pauli word ``Observable`` instances\n        \"\"\"\n\n        if self.adj_matrix is None:\n            self.adj_matrix = self.complement_adj_matrix_for_operator()\n\n        coloured_binary_paulis = self.graph_colourer(self.binary_observables, self.adj_matrix)\n\n        self.grouped_paulis = [\n            [binary_to_pauli(pauli_word, wire_map=self._wire_map) for pauli_word in grouping]\n            for grouping in coloured_binary_paulis.values()\n        ]\n\n        return self.grouped_paulis\n\n\ndef group_observables(observables, coefficients=None, grouping_type=\"qwc\", method=\"rlf\"):\n    \"\"\"Partitions a list of observables (Pauli operations and tensor products thereof) into\n    groupings according to a binary relation (qubit-wise commuting, fully-commuting, or\n    anticommuting).\n\n    Partitions are found by 1) mapping the list of observables to a graph where vertices represent\n    observables and edges encode the binary relation, then 2) solving minimum clique cover for the\n    graph using graph-colouring heuristic algorithms.\n\n    Args:\n        observables (list[Observable]): a list of Pauli word ``Observable`` instances (Pauli\n            operation instances and :class:`~.Tensor` instances thereof)\n        coefficients (list[float]): A list of float coefficients. If not specified,\n            output ``partitioned_coeffs`` is not returned.\n        grouping_type (str): The type of binary relation between Pauli words.\n            Can be ``'qwc'``, ``'commuting'``, or ``'anticommuting'``.\n        method (str): the graph coloring heuristic to use in solving minimum clique cover, which\n            can be ``'lf'`` (Largest First) or ``'rlf'`` (Recursive Largest First)\n\n    Returns:\n       tuple:\n\n           * list[list[Observable]]: A list of the obtained groupings. Each grouping\n             is itself a list of Pauli word ``Observable`` instances.\n           * list[list[float]]: A list of coefficient groupings. Each coefficient\n             grouping is itself a list of the grouping's corresponding coefficients. This is only\n             output if coefficients are specified.\n\n    Raises:\n        IndexError: if the input list of coefficients is not of the same length as the input list\n            of Pauli words\n\n    **Example**\n\n    >>> obs = [qml.PauliY(0), qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(1)]\n    >>> coeffs = [1.43, 4.21, 0.97]\n    >>> obs_groupings, coeffs_groupings = group_observables(obs, coeffs, 'anticommuting', 'lf')\n    >>> obs_groupings\n    [[PauliZ(wires=[1]), PauliX(wires=[0]) @ PauliX(wires=[1])],\n     [PauliY(wires=[0])]]\n    >>> coeffs_groupings\n    [[0.97, 4.21], [1.43]]\n    \"\"\"\n\n    if coefficients is not None:\n        if len(coefficients) != len(observables):\n            raise IndexError(\n                \"The coefficients list must be the same length as the observables list.\"\n            )\n\n    pauli_grouping = PauliGroupingStrategy(\n        observables, grouping_type=grouping_type, graph_colourer=method\n    )\n    partitioned_paulis = pauli_grouping.colour_pauli_graph()\n\n    if coefficients is None:\n        return partitioned_paulis\n\n    partitioned_coeffs = [[0] * len(g) for g in partitioned_paulis]\n\n    for i, partition in enumerate(partitioned_paulis):\n        for j, pauli_word in enumerate(partition):\n            for observable in observables:\n                if are_identical_pauli_words(pauli_word, observable):\n                    partitioned_coeffs[i][j] = coefficients[observables.index(observable)]\n                    break\n\n    return partitioned_paulis, partitioned_coeffs\n", "path": "pennylane/grouping/group_observables.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\"\"\"\nThis module contains the high-level Pauli-word-partitioning functionality used in measurement optimization.\n\"\"\"\n\nfrom copy import copy\n\nimport numpy as np\n\nfrom pennylane.grouping.graph_colouring import largest_first, recursive_largest_first\nfrom pennylane.grouping.utils import (\n    are_identical_pauli_words,\n    binary_to_pauli,\n    observables_to_binary_matrix,\n    qwc_complement_adj_matrix,\n)\nfrom pennylane.wires import Wires\n\nGROUPING_TYPES = frozenset([\"qwc\", \"commuting\", \"anticommuting\"])\nGRAPH_COLOURING_METHODS = {\"lf\": largest_first, \"rlf\": recursive_largest_first}\n\n\nclass PauliGroupingStrategy:  # pylint: disable=too-many-instance-attributes\n    \"\"\"\n    Class for partitioning a list of Pauli words according to some binary symmetric relation.\n\n    Partitions are defined by the binary symmetric relation of interest, e.g., all Pauli words in a\n    partition being mutually commuting. The partitioning is accomplished by formulating the list of\n    Pauli words as a graph where nodes represent Pauli words and edges between nodes denotes that\n    the two corresponding Pauli words satisfy the symmetric binary relation.\n\n    Obtaining the fewest number of partitions such that all Pauli terms within a partition mutually\n    satisfy the binary relation can then be accomplished by finding a partition of the graph nodes\n    such that each partition is a fully connected subgraph (a \"clique\"). The problem of finding the\n    optimal partitioning, i.e., the fewest number of cliques, is the minimum clique cover (MCC)\n    problem. The solution of MCC may be found by graph colouring on the complementary graph. Both\n    MCC and graph colouring are NP-Hard, so heuristic graph colouring algorithms are employed to\n    find approximate solutions in polynomial time.\n\n    Args:\n        observables (list[Observable]): a list of Pauli words to be partitioned according to a\n            grouping strategy\n        grouping_type (str): the binary relation used to define partitions of\n            the Pauli words, can be ``'qwc'`` (qubit-wise commuting), ``'commuting'``, or\n            ``'anticommuting'``.\n        graph_colourer (str): the heuristic algorithm to employ for graph\n            colouring, can be ``'lf'`` (Largest First) or ``'rlf'`` (Recursive\n            Largest First)\n\n    Raises:\n        ValueError: if arguments specified for ``grouping_type`` or\n            ``graph_colourer`` are not recognized\n    \"\"\"\n\n    def __init__(self, observables, grouping_type=\"qwc\", graph_colourer=\"rlf\"):\n\n        if grouping_type.lower() not in GROUPING_TYPES:\n            raise ValueError(\n                \"Grouping type must be one of: {}, instead got {}.\".format(\n                    GROUPING_TYPES, grouping_type\n                )\n            )\n\n        self.grouping_type = grouping_type.lower()\n\n        if graph_colourer.lower() not in GRAPH_COLOURING_METHODS.keys():\n            raise ValueError(\n                \"Graph colouring method must be one of: {}, instead got {}.\".format(\n                    list(GRAPH_COLOURING_METHODS.keys()), graph_colourer\n                )\n            )\n\n        self.graph_colourer = GRAPH_COLOURING_METHODS[graph_colourer.lower()]\n        self.observables = observables\n        self._wire_map = None\n        self._n_qubits = None\n        self.binary_observables = None\n        self.adj_matrix = None\n        self.grouped_paulis = None\n\n    def binary_repr(self, n_qubits=None, wire_map=None):\n        \"\"\"Converts the list of Pauli words to a binary matrix.\n\n        Args:\n            n_qubits (int): number of qubits to specify dimension of binary vector representation\n            wire_map (dict): dictionary containing all wire labels used in the Pauli word as keys,\n                and unique integer labels as their values\n\n        Returns:\n            array[int]: a column matrix of the Pauli words in binary vector representation\n        \"\"\"\n\n        if wire_map is None:\n            self._wire_map = {\n                wire: c\n                for c, wire in enumerate(\n                    Wires.all_wires([obs.wires for obs in self.observables]).tolist()\n                )\n            }\n\n        else:\n            self._wire_map = wire_map\n\n        self._n_qubits = n_qubits\n\n        return observables_to_binary_matrix(self.observables, n_qubits, self._wire_map)\n\n    def complement_adj_matrix_for_operator(self):\n        \"\"\"Constructs the adjacency matrix for the complement of the Pauli graph.\n\n        The adjacency matrix for an undirected graph of N vertices is an N by N symmetric binary\n        matrix, where matrix elements of 1 denote an edge, and matrix elements of 0 denote no edge.\n\n        Returns:\n            array[int]: the square and symmetric adjacency matrix\n        \"\"\"\n\n        if self.binary_observables is None:\n            self.binary_observables = self.binary_repr()\n\n        n_qubits = int(np.shape(self.binary_observables)[1] / 2)\n\n        if self.grouping_type == \"qwc\":\n            adj = qwc_complement_adj_matrix(self.binary_observables)\n\n        elif self.grouping_type in frozenset([\"commuting\", \"anticommuting\"]):\n            symplectic_form = np.block(\n                [\n                    [np.zeros((n_qubits, n_qubits)), np.eye(n_qubits)],\n                    [np.eye(n_qubits), np.zeros((n_qubits, n_qubits))],\n                ]\n            )\n            mat_prod = (\n                self.binary_observables @ symplectic_form @ np.transpose(self.binary_observables)\n            )\n\n            if self.grouping_type == \"commuting\":\n\n                adj = mat_prod % 2\n\n            elif self.grouping_type == \"anticommuting\":\n\n                adj = (mat_prod + 1) % 2\n                np.fill_diagonal(adj, 0)\n\n        return adj\n\n    def colour_pauli_graph(self):\n        \"\"\"\n        Runs the graph colouring heuristic algorithm to obtain the partitioned Pauli words.\n\n        Returns:\n            list[list[Observable]]: a list of the obtained groupings. Each grouping is itself a\n            list of Pauli word ``Observable`` instances\n        \"\"\"\n\n        if self.adj_matrix is None:\n            self.adj_matrix = self.complement_adj_matrix_for_operator()\n\n        coloured_binary_paulis = self.graph_colourer(self.binary_observables, self.adj_matrix)\n\n        self.grouped_paulis = [\n            [binary_to_pauli(pauli_word, wire_map=self._wire_map) for pauli_word in grouping]\n            for grouping in coloured_binary_paulis.values()\n        ]\n\n        return self.grouped_paulis\n\n\ndef group_observables(observables, coefficients=None, grouping_type=\"qwc\", method=\"rlf\"):\n    \"\"\"Partitions a list of observables (Pauli operations and tensor products thereof) into\n    groupings according to a binary relation (qubit-wise commuting, fully-commuting, or\n    anticommuting).\n\n    Partitions are found by 1) mapping the list of observables to a graph where vertices represent\n    observables and edges encode the binary relation, then 2) solving minimum clique cover for the\n    graph using graph-colouring heuristic algorithms.\n\n    Args:\n        observables (list[Observable]): a list of Pauli word ``Observable`` instances (Pauli\n            operation instances and :class:`~.Tensor` instances thereof)\n        coefficients (list[float]): A list of float coefficients. If not specified,\n            output ``partitioned_coeffs`` is not returned.\n        grouping_type (str): The type of binary relation between Pauli words.\n            Can be ``'qwc'``, ``'commuting'``, or ``'anticommuting'``.\n        method (str): the graph coloring heuristic to use in solving minimum clique cover, which\n            can be ``'lf'`` (Largest First) or ``'rlf'`` (Recursive Largest First)\n\n    Returns:\n       tuple:\n\n           * list[list[Observable]]: A list of the obtained groupings. Each grouping\n             is itself a list of Pauli word ``Observable`` instances.\n           * list[list[float]]: A list of coefficient groupings. Each coefficient\n             grouping is itself a list of the grouping's corresponding coefficients. This is only\n             output if coefficients are specified.\n\n    Raises:\n        IndexError: if the input list of coefficients is not of the same length as the input list\n            of Pauli words\n\n    **Example**\n\n    >>> obs = [qml.PauliY(0), qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(1)]\n    >>> coeffs = [1.43, 4.21, 0.97]\n    >>> obs_groupings, coeffs_groupings = group_observables(obs, coeffs, 'anticommuting', 'lf')\n    >>> obs_groupings\n    [[PauliZ(wires=[1]), PauliX(wires=[0]) @ PauliX(wires=[1])],\n     [PauliY(wires=[0])]]\n    >>> coeffs_groupings\n    [[0.97, 4.21], [1.43]]\n    \"\"\"\n\n    if coefficients is not None:\n        if len(coefficients) != len(observables):\n            raise IndexError(\n                \"The coefficients list must be the same length as the observables list.\"\n            )\n\n    pauli_grouping = PauliGroupingStrategy(\n        observables, grouping_type=grouping_type, graph_colourer=method\n    )\n    partitioned_paulis = pauli_grouping.colour_pauli_graph()\n\n    if coefficients is None:\n        return partitioned_paulis\n\n    partitioned_coeffs = [[0] * len(g) for g in partitioned_paulis]\n\n    observables = copy(observables)\n    coefficients = copy(coefficients)\n    for i, partition in enumerate(partitioned_paulis):\n        for j, pauli_word in enumerate(partition):\n            for observable in observables:\n                if are_identical_pauli_words(pauli_word, observable):\n                    ind = observables.index(observable)\n                    partitioned_coeffs[i][j] = coefficients[ind]\n                    observables.pop(ind)\n                    coefficients.pop(ind)\n                    break\n\n    return partitioned_paulis, partitioned_coeffs\n", "path": "pennylane/grouping/group_observables.py"}]}