We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] compile pre-decomposes at most once
### Expected behavior

I would expect that if I do qml.compile() of a template and its adjoint template, the decompositions would be equivalent but adjoined. However the depth of the decomposition is different:

<img width="300" alt="Captura de pantalla 2023-11-07 a las 13 58 38" src="https://github.com/PennyLaneAI/pennylane/assets/65235481/b6d7525e-e751-47ea-824e-d1bda5701f73">

<img width="300" alt="Captura de pantalla 2023-11-07 a las 13 58 49" src="https://github.com/PennyLaneAI/pennylane/assets/65235481/967a1e09-0203-4501-b0c2-3c2e345cc5d7">

As we can see in one case it expands the QFT and in another it does not. I would have liked that they were equivalent so in both cases it shows (QFT or QFT^t)



### Actual behavior

Explained above

### Additional information

_No response_

### Source code

```shell
import pennylane as qml

dev = qml.device("default.qubit")

@qml.qnode(dev)
@qml.compile()
def circuit():
  #qml.QuantumPhaseEstimation(qml.PauliZ(0), estimation_wires = range(1,3))
  qml.adjoint(qml.QuantumPhaseEstimation)(qml.PauliZ(0), estimation_wires = range(1,3))
  return qml.state()

qml.draw_mpl(circuit)()
```


### Tracebacks

_No response_

### System information

```shell
Name: PennyLane
Version: 0.33.0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/PennyLaneAI/pennylane
Author: 
Author-email: 
License: Apache License 2.0
Location: /usr/local/lib/python3.10/dist-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions
Required-by: PennyLane-Lightning

Platform info:           Linux-5.15.120+-x86_64-with-glibc2.35
Python version:          3.10.12
Numpy version:           1.23.5
Scipy version:           1.11.3
Installed devices:
```


### Existing GitHub issues

- [X] I have searched existing GitHub issues to make sure the issue does not already exist.

--- 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/compile.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 """Code for the high-level quantum function transform that executes compilation."""
15 # pylint: disable=too-many-branches
16 from functools import partial
17 from typing import Sequence, Callable
18 
19 from pennylane.queuing import QueuingManager
20 from pennylane.ops import __all__ as all_ops
21 from pennylane.tape import QuantumTape
22 from pennylane.transforms.core import transform, TransformDispatcher
23 from pennylane.transforms.optimization import (
24     cancel_inverses,
25     commute_controlled,
26     merge_rotations,
27     remove_barrier,
28 )
29 
30 
31 default_pipeline = [commute_controlled, cancel_inverses, merge_rotations, remove_barrier]
32 
33 
34 @transform
35 def compile(
36     tape: QuantumTape, pipeline=None, basis_set=None, num_passes=1, expand_depth=5
37 ) -> (Sequence[QuantumTape], Callable):
38     """Compile a circuit by applying a series of transforms to a quantum function.
39 
40     The default set of transforms includes (in order):
41 
42     - pushing all commuting single-qubit gates as far right as possible
43       (:func:`~pennylane.transforms.commute_controlled`)
44     - cancellation of adjacent inverse gates
45       (:func:`~pennylane.transforms.cancel_inverses`)
46     - merging adjacent rotations of the same type
47       (:func:`~pennylane.transforms.merge_rotations`)
48 
49     Args:
50         tape (QNode or QuantumTape or Callable): A quantum circuit.
51         pipeline (list[Callable]): A list of
52             tape and/or quantum function transforms to apply.
53         basis_set (list[str]): A list of basis gates. When expanding the tape,
54             expansion will continue until gates in the specific set are
55             reached. If no basis set is specified, no expansion will be done.
56         num_passes (int): The number of times to apply the set of transforms in
57             ``pipeline``. The default is to perform each transform once;
58             however, doing so may produce a new circuit where applying the set
59             of transforms again may yield further improvement, so the number of
60             such passes can be adjusted.
61         expand_depth (int): When ``basis_set`` is specified, the depth to use
62             for tape expansion into the basis gates.
63 
64     Returns:
65         qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]: The compiled circuit. The output type is explained in :func:`qml.transform <pennylane.transform>`.
66 
67     **Example**
68 
69     >>> dev = qml.device('default.qubit', wires=[0, 1, 2])
70 
71     You can apply the transform directly on a :class:`QNode`:
72 
73     .. code-block:: python
74 
75         @compile
76         @qml.qnode(device=dev)
77         def circuit(x, y, z):
78             qml.Hadamard(wires=0)
79             qml.Hadamard(wires=1)
80             qml.Hadamard(wires=2)
81             qml.RZ(z, wires=2)
82             qml.CNOT(wires=[2, 1])
83             qml.RX(z, wires=0)
84             qml.CNOT(wires=[1, 0])
85             qml.RX(x, wires=0)
86             qml.CNOT(wires=[1, 0])
87             qml.RZ(-z, wires=2)
88             qml.RX(y, wires=2)
89             qml.PauliY(wires=2)
90             qml.CY(wires=[1, 2])
91             return qml.expval(qml.PauliZ(wires=0))
92 
93     The default compilation pipeline is applied before execution.
94 
95     Consider the following quantum function:
96 
97     .. code-block:: python
98 
99         def qfunc(x, y, z):
100             qml.Hadamard(wires=0)
101             qml.Hadamard(wires=1)
102             qml.Hadamard(wires=2)
103             qml.RZ(z, wires=2)
104             qml.CNOT(wires=[2, 1])
105             qml.RX(z, wires=0)
106             qml.CNOT(wires=[1, 0])
107             qml.RX(x, wires=0)
108             qml.CNOT(wires=[1, 0])
109             qml.RZ(-z, wires=2)
110             qml.RX(y, wires=2)
111             qml.PauliY(wires=2)
112             qml.CY(wires=[1, 2])
113             return qml.expval(qml.PauliZ(wires=0))
114 
115     Visually, the original function looks like this:
116 
117     >>> qnode = qml.QNode(qfunc, dev)
118     >>> print(qml.draw(qnode)(0.2, 0.3, 0.4))
119     0: ──H──RX(0.40)────╭X──────────RX(0.20)─╭X────┤  <Z>
120     1: ──H───────────╭X─╰●───────────────────╰●─╭●─┤
121     2: ──H──RZ(0.40)─╰●──RZ(-0.40)──RX(0.30)──Y─╰Y─┤
122 
123     We can compile it down to a smaller set of gates using the ``qml.compile``
124     transform.
125 
126     >>> compiled_qfunc = qml.compile(qfunc)
127     >>> compiled_qnode = qml.QNode(compiled_qfunc, dev)
128     >>> print(qml.draw(compiled_qnode)(0.2, 0.3, 0.4))
129     0: ──H──RX(0.60)─────────────────┤  <Z>
130     1: ──H─╭X──────────────────╭●────┤
131     2: ──H─╰●─────────RX(0.30)─╰Y──Y─┤
132 
133     You can change up the set of transforms by passing a custom ``pipeline`` to
134     ``qml.compile``. The pipeline is a list of transform functions. Furthermore,
135     you can specify a number of passes (repetitions of the pipeline), and a list
136     of gates into which the compiler will first attempt to decompose the
137     existing operations prior to applying any optimization transforms.
138 
139     .. code-block:: python3
140 
141         compiled_qfunc = qml.compile(
142             pipeline=[
143                 partial(qml.transforms.commute_controlled, direction="left"),
144                 partial(qml.transforms.merge_rotations, atol=1e-6),
145                 qml.transforms.cancel_inverses
146             ],
147             basis_set=["CNOT", "RX", "RY", "RZ"],
148             num_passes=2
149         )(qfunc)
150 
151         compiled_qnode = qml.QNode(compiled_qfunc, dev)
152 
153         print(qml.draw(compiled_qnode)(0.2, 0.3, 0.4))
154 
155     .. code-block::
156 
157         0: ──RZ(1.57)──RX(1.57)──RZ(1.57)──RX(0.60)─────────────────────────────────────────────────────
158         1: ──RZ(1.57)──RX(1.57)──RZ(1.57)─╭X─────────RZ(1.57)─────────────────────────────────────────╭●
159         2: ──RZ(1.57)──RX(1.57)──RZ(1.57)─╰●─────────RX(0.30)──RZ(1.57)──RY(3.14)──RZ(1.57)──RY(1.57)─╰X
160 
161         ────────────────┤  <Z>
162         ─────────────╭●─┤
163         ───RY(-1.57)─╰X─┤
164 
165     """
166     # Ensure that everything in the pipeline is a valid qfunc or tape transform
167     if pipeline is None:
168         pipeline = default_pipeline
169     else:
170         for p in pipeline:
171             p_func = p.func if isinstance(p, partial) else p
172             if not isinstance(p_func, TransformDispatcher):
173                 raise ValueError("Invalid transform function {p} passed to compile.")
174 
175     if num_passes < 1 or not isinstance(num_passes, int):
176         raise ValueError("Number of passes must be an integer with value at least 1.")
177 
178     # Expand the tape; this is done to unroll any templates that may be present,
179     # as well as to decompose over a specified basis set
180     # First, though, we have to stop whatever tape may be recording so that we
181     # don't queue anything as a result of the expansion or transform pipeline
182 
183     with QueuingManager.stop_recording():
184         if basis_set is not None:
185             expanded_tape = tape.expand(
186                 depth=expand_depth, stop_at=lambda obj: obj.name in basis_set
187             )
188         else:
189             # Expands out anything that is not a single operation (i.e., the templates)
190             # expand barriers when `only_visual=True`
191             def stop_at(obj):
192                 return (obj.name in all_ops) and (not getattr(obj, "only_visual", False))
193 
194             expanded_tape = tape.expand(stop_at=stop_at)
195 
196         # Apply the full set of compilation transforms num_passes times
197         for _ in range(num_passes):
198             for transf in pipeline:
199                 tapes, _ = transf(expanded_tape)
200                 expanded_tape = tapes[0]
201 
202     new_tape = type(tape)(
203         expanded_tape.operations, expanded_tape.measurements, shots=expanded_tape.shots
204     )
205 
206     def null_postprocessing(results):
207         """A postprocesing function returned by a transform that only converts the batch of results
208         into a result for a single ``QuantumTape``.
209         """
210         return results[0]
211 
212     return [new_tape], null_postprocessing
213
```
--- 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/compile.py b/pennylane/transforms/compile.py
--- a/pennylane/transforms/compile.py
+++ b/pennylane/transforms/compile.py
@@ -52,14 +52,15 @@
             tape and/or quantum function transforms to apply.
         basis_set (list[str]): A list of basis gates. When expanding the tape,
             expansion will continue until gates in the specific set are
-            reached. If no basis set is specified, no expansion will be done.
+            reached. If no basis set is specified, a default of
+            ``pennylane.ops.__all__`` will be used. This decomposes templates and
+            operator arithmetic.
         num_passes (int): The number of times to apply the set of transforms in
             ``pipeline``. The default is to perform each transform once;
             however, doing so may produce a new circuit where applying the set
             of transforms again may yield further improvement, so the number of
             such passes can be adjusted.
-        expand_depth (int): When ``basis_set`` is specified, the depth to use
-            for tape expansion into the basis gates.
+        expand_depth (int): The depth to use for tape expansion into the basis gates.
 
     Returns:
         qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]: The compiled circuit. The output type is explained in :func:`qml.transform <pennylane.transform>`.
@@ -181,17 +182,12 @@
     # don't queue anything as a result of the expansion or transform pipeline
 
     with QueuingManager.stop_recording():
-        if basis_set is not None:
-            expanded_tape = tape.expand(
-                depth=expand_depth, stop_at=lambda obj: obj.name in basis_set
-            )
-        else:
-            # Expands out anything that is not a single operation (i.e., the templates)
-            # expand barriers when `only_visual=True`
-            def stop_at(obj):
-                return (obj.name in all_ops) and (not getattr(obj, "only_visual", False))
-
-            expanded_tape = tape.expand(stop_at=stop_at)
+        basis_set = basis_set or all_ops
+
+        def stop_at(obj):
+            return obj.name in basis_set and (not getattr(obj, "only_visual", False))
+
+        expanded_tape = tape.expand(depth=expand_depth, stop_at=stop_at)
 
         # Apply the full set of compilation transforms num_passes times
         for _ in range(num_passes):

{"golden_diff": "diff --git a/pennylane/transforms/compile.py b/pennylane/transforms/compile.py\n--- a/pennylane/transforms/compile.py\n+++ b/pennylane/transforms/compile.py\n@@ -52,14 +52,15 @@\n             tape and/or quantum function transforms to apply.\n         basis_set (list[str]): A list of basis gates. When expanding the tape,\n             expansion will continue until gates in the specific set are\n-            reached. If no basis set is specified, no expansion will be done.\n+            reached. If no basis set is specified, a default of\n+            ``pennylane.ops.__all__`` will be used. This decomposes templates and\n+            operator arithmetic.\n         num_passes (int): The number of times to apply the set of transforms in\n             ``pipeline``. The default is to perform each transform once;\n             however, doing so may produce a new circuit where applying the set\n             of transforms again may yield further improvement, so the number of\n             such passes can be adjusted.\n-        expand_depth (int): When ``basis_set`` is specified, the depth to use\n-            for tape expansion into the basis gates.\n+        expand_depth (int): The depth to use for tape expansion into the basis gates.\n \n     Returns:\n         qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]: The compiled circuit. The output type is explained in :func:`qml.transform <pennylane.transform>`.\n@@ -181,17 +182,12 @@\n     # don't queue anything as a result of the expansion or transform pipeline\n \n     with QueuingManager.stop_recording():\n-        if basis_set is not None:\n-            expanded_tape = tape.expand(\n-                depth=expand_depth, stop_at=lambda obj: obj.name in basis_set\n-            )\n-        else:\n-            # Expands out anything that is not a single operation (i.e., the templates)\n-            # expand barriers when `only_visual=True`\n-            def stop_at(obj):\n-                return (obj.name in all_ops) and (not getattr(obj, \"only_visual\", False))\n-\n-            expanded_tape = tape.expand(stop_at=stop_at)\n+        basis_set = basis_set or all_ops\n+\n+        def stop_at(obj):\n+            return obj.name in basis_set and (not getattr(obj, \"only_visual\", False))\n+\n+        expanded_tape = tape.expand(depth=expand_depth, stop_at=stop_at)\n \n         # Apply the full set of compilation transforms num_passes times\n         for _ in range(num_passes):\n", "issue": "[BUG] compile pre-decomposes at most once\n### Expected behavior\r\n\r\nI would expect that if I do qml.compile() of a template and its adjoint template, the decompositions would be equivalent but adjoined. However the depth of the decomposition is different:\r\n\r\n<img width=\"300\" alt=\"Captura de pantalla 2023-11-07 a las 13 58 38\" src=\"https://github.com/PennyLaneAI/pennylane/assets/65235481/b6d7525e-e751-47ea-824e-d1bda5701f73\">\r\n\r\n<img width=\"300\" alt=\"Captura de pantalla 2023-11-07 a las 13 58 49\" src=\"https://github.com/PennyLaneAI/pennylane/assets/65235481/967a1e09-0203-4501-b0c2-3c2e345cc5d7\">\r\n\r\nAs we can see in one case it expands the QFT and in another it does not. I would have liked that they were equivalent so in both cases it shows (QFT or QFT^t)\r\n\r\n\r\n\r\n### Actual behavior\r\n\r\nExplained above\r\n\r\n### Additional information\r\n\r\n_No response_\r\n\r\n### Source code\r\n\r\n```shell\r\nimport pennylane as qml\r\n\r\ndev = qml.device(\"default.qubit\")\r\n\r\n@qml.qnode(dev)\r\n@qml.compile()\r\ndef circuit():\r\n  #qml.QuantumPhaseEstimation(qml.PauliZ(0), estimation_wires = range(1,3))\r\n  qml.adjoint(qml.QuantumPhaseEstimation)(qml.PauliZ(0), estimation_wires = range(1,3))\r\n  return qml.state()\r\n\r\nqml.draw_mpl(circuit)()\r\n```\r\n\r\n\r\n### Tracebacks\r\n\r\n_No response_\r\n\r\n### System information\r\n\r\n```shell\r\nName: PennyLane\r\nVersion: 0.33.0\r\nSummary: PennyLane is a Python quantum machine learning library by Xanadu Inc.\r\nHome-page: https://github.com/PennyLaneAI/pennylane\r\nAuthor: \r\nAuthor-email: \r\nLicense: Apache License 2.0\r\nLocation: /usr/local/lib/python3.10/dist-packages\r\nRequires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions\r\nRequired-by: PennyLane-Lightning\r\n\r\nPlatform info:           Linux-5.15.120+-x86_64-with-glibc2.35\r\nPython version:          3.10.12\r\nNumpy version:           1.23.5\r\nScipy version:           1.11.3\r\nInstalled devices:\r\n```\r\n\r\n\r\n### Existing GitHub issues\r\n\r\n- [X] I have searched existing GitHub issues to make sure the issue does not already exist.\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\"\"\"Code for the high-level quantum function transform that executes compilation.\"\"\"\n# pylint: disable=too-many-branches\nfrom functools import partial\nfrom typing import Sequence, Callable\n\nfrom pennylane.queuing import QueuingManager\nfrom pennylane.ops import __all__ as all_ops\nfrom pennylane.tape import QuantumTape\nfrom pennylane.transforms.core import transform, TransformDispatcher\nfrom pennylane.transforms.optimization import (\n    cancel_inverses,\n    commute_controlled,\n    merge_rotations,\n    remove_barrier,\n)\n\n\ndefault_pipeline = [commute_controlled, cancel_inverses, merge_rotations, remove_barrier]\n\n\n@transform\ndef compile(\n    tape: QuantumTape, pipeline=None, basis_set=None, num_passes=1, expand_depth=5\n) -> (Sequence[QuantumTape], Callable):\n    \"\"\"Compile a circuit by applying a series of transforms to a quantum function.\n\n    The default set of transforms includes (in order):\n\n    - pushing all commuting single-qubit gates as far right as possible\n      (:func:`~pennylane.transforms.commute_controlled`)\n    - cancellation of adjacent inverse gates\n      (:func:`~pennylane.transforms.cancel_inverses`)\n    - merging adjacent rotations of the same type\n      (:func:`~pennylane.transforms.merge_rotations`)\n\n    Args:\n        tape (QNode or QuantumTape or Callable): A quantum circuit.\n        pipeline (list[Callable]): A list of\n            tape and/or quantum function transforms to apply.\n        basis_set (list[str]): A list of basis gates. When expanding the tape,\n            expansion will continue until gates in the specific set are\n            reached. If no basis set is specified, no expansion will be done.\n        num_passes (int): The number of times to apply the set of transforms in\n            ``pipeline``. The default is to perform each transform once;\n            however, doing so may produce a new circuit where applying the set\n            of transforms again may yield further improvement, so the number of\n            such passes can be adjusted.\n        expand_depth (int): When ``basis_set`` is specified, the depth to use\n            for tape expansion into the basis gates.\n\n    Returns:\n        qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]: The compiled circuit. The output type is explained in :func:`qml.transform <pennylane.transform>`.\n\n    **Example**\n\n    >>> dev = qml.device('default.qubit', wires=[0, 1, 2])\n\n    You can apply the transform directly on a :class:`QNode`:\n\n    .. code-block:: python\n\n        @compile\n        @qml.qnode(device=dev)\n        def circuit(x, y, z):\n            qml.Hadamard(wires=0)\n            qml.Hadamard(wires=1)\n            qml.Hadamard(wires=2)\n            qml.RZ(z, wires=2)\n            qml.CNOT(wires=[2, 1])\n            qml.RX(z, wires=0)\n            qml.CNOT(wires=[1, 0])\n            qml.RX(x, wires=0)\n            qml.CNOT(wires=[1, 0])\n            qml.RZ(-z, wires=2)\n            qml.RX(y, wires=2)\n            qml.PauliY(wires=2)\n            qml.CY(wires=[1, 2])\n            return qml.expval(qml.PauliZ(wires=0))\n\n    The default compilation pipeline is applied before execution.\n\n    Consider the following quantum function:\n\n    .. code-block:: python\n\n        def qfunc(x, y, z):\n            qml.Hadamard(wires=0)\n            qml.Hadamard(wires=1)\n            qml.Hadamard(wires=2)\n            qml.RZ(z, wires=2)\n            qml.CNOT(wires=[2, 1])\n            qml.RX(z, wires=0)\n            qml.CNOT(wires=[1, 0])\n            qml.RX(x, wires=0)\n            qml.CNOT(wires=[1, 0])\n            qml.RZ(-z, wires=2)\n            qml.RX(y, wires=2)\n            qml.PauliY(wires=2)\n            qml.CY(wires=[1, 2])\n            return qml.expval(qml.PauliZ(wires=0))\n\n    Visually, the original function looks like this:\n\n    >>> qnode = qml.QNode(qfunc, dev)\n    >>> print(qml.draw(qnode)(0.2, 0.3, 0.4))\n    0: \u2500\u2500H\u2500\u2500RX(0.40)\u2500\u2500\u2500\u2500\u256dX\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500RX(0.20)\u2500\u256dX\u2500\u2500\u2500\u2500\u2524  <Z>\n    1: \u2500\u2500H\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256dX\u2500\u2570\u25cf\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2570\u25cf\u2500\u256d\u25cf\u2500\u2524\n    2: \u2500\u2500H\u2500\u2500RZ(0.40)\u2500\u2570\u25cf\u2500\u2500RZ(-0.40)\u2500\u2500RX(0.30)\u2500\u2500Y\u2500\u2570Y\u2500\u2524\n\n    We can compile it down to a smaller set of gates using the ``qml.compile``\n    transform.\n\n    >>> compiled_qfunc = qml.compile(qfunc)\n    >>> compiled_qnode = qml.QNode(compiled_qfunc, dev)\n    >>> print(qml.draw(compiled_qnode)(0.2, 0.3, 0.4))\n    0: \u2500\u2500H\u2500\u2500RX(0.60)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524  <Z>\n    1: \u2500\u2500H\u2500\u256dX\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256d\u25cf\u2500\u2500\u2500\u2500\u2524\n    2: \u2500\u2500H\u2500\u2570\u25cf\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500RX(0.30)\u2500\u2570Y\u2500\u2500Y\u2500\u2524\n\n    You can change up the set of transforms by passing a custom ``pipeline`` to\n    ``qml.compile``. The pipeline is a list of transform functions. Furthermore,\n    you can specify a number of passes (repetitions of the pipeline), and a list\n    of gates into which the compiler will first attempt to decompose the\n    existing operations prior to applying any optimization transforms.\n\n    .. code-block:: python3\n\n        compiled_qfunc = qml.compile(\n            pipeline=[\n                partial(qml.transforms.commute_controlled, direction=\"left\"),\n                partial(qml.transforms.merge_rotations, atol=1e-6),\n                qml.transforms.cancel_inverses\n            ],\n            basis_set=[\"CNOT\", \"RX\", \"RY\", \"RZ\"],\n            num_passes=2\n        )(qfunc)\n\n        compiled_qnode = qml.QNode(compiled_qfunc, dev)\n\n        print(qml.draw(compiled_qnode)(0.2, 0.3, 0.4))\n\n    .. code-block::\n\n        0: \u2500\u2500RZ(1.57)\u2500\u2500RX(1.57)\u2500\u2500RZ(1.57)\u2500\u2500RX(0.60)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        1: \u2500\u2500RZ(1.57)\u2500\u2500RX(1.57)\u2500\u2500RZ(1.57)\u2500\u256dX\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500RZ(1.57)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256d\u25cf\n        2: \u2500\u2500RZ(1.57)\u2500\u2500RX(1.57)\u2500\u2500RZ(1.57)\u2500\u2570\u25cf\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500RX(0.30)\u2500\u2500RZ(1.57)\u2500\u2500RY(3.14)\u2500\u2500RZ(1.57)\u2500\u2500RY(1.57)\u2500\u2570X\n\n        \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524  <Z>\n        \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256d\u25cf\u2500\u2524\n        \u2500\u2500\u2500RY(-1.57)\u2500\u2570X\u2500\u2524\n\n    \"\"\"\n    # Ensure that everything in the pipeline is a valid qfunc or tape transform\n    if pipeline is None:\n        pipeline = default_pipeline\n    else:\n        for p in pipeline:\n            p_func = p.func if isinstance(p, partial) else p\n            if not isinstance(p_func, TransformDispatcher):\n                raise ValueError(\"Invalid transform function {p} passed to compile.\")\n\n    if num_passes < 1 or not isinstance(num_passes, int):\n        raise ValueError(\"Number of passes must be an integer with value at least 1.\")\n\n    # Expand the tape; this is done to unroll any templates that may be present,\n    # as well as to decompose over a specified basis set\n    # First, though, we have to stop whatever tape may be recording so that we\n    # don't queue anything as a result of the expansion or transform pipeline\n\n    with QueuingManager.stop_recording():\n        if basis_set is not None:\n            expanded_tape = tape.expand(\n                depth=expand_depth, stop_at=lambda obj: obj.name in basis_set\n            )\n        else:\n            # Expands out anything that is not a single operation (i.e., the templates)\n            # expand barriers when `only_visual=True`\n            def stop_at(obj):\n                return (obj.name in all_ops) and (not getattr(obj, \"only_visual\", False))\n\n            expanded_tape = tape.expand(stop_at=stop_at)\n\n        # Apply the full set of compilation transforms num_passes times\n        for _ in range(num_passes):\n            for transf in pipeline:\n                tapes, _ = transf(expanded_tape)\n                expanded_tape = tapes[0]\n\n    new_tape = type(tape)(\n        expanded_tape.operations, expanded_tape.measurements, shots=expanded_tape.shots\n    )\n\n    def null_postprocessing(results):\n        \"\"\"A postprocesing function returned by a transform that only converts the batch of results\n        into a result for a single ``QuantumTape``.\n        \"\"\"\n        return results[0]\n\n    return [new_tape], null_postprocessing\n", "path": "pennylane/transforms/compile.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\"\"\"Code for the high-level quantum function transform that executes compilation.\"\"\"\n# pylint: disable=too-many-branches\nfrom functools import partial\nfrom typing import Sequence, Callable\n\nfrom pennylane.queuing import QueuingManager\nfrom pennylane.ops import __all__ as all_ops\nfrom pennylane.tape import QuantumTape\nfrom pennylane.transforms.core import transform, TransformDispatcher\nfrom pennylane.transforms.optimization import (\n    cancel_inverses,\n    commute_controlled,\n    merge_rotations,\n    remove_barrier,\n)\n\n\ndefault_pipeline = [commute_controlled, cancel_inverses, merge_rotations, remove_barrier]\n\n\n@transform\ndef compile(\n    tape: QuantumTape, pipeline=None, basis_set=None, num_passes=1, expand_depth=5\n) -> (Sequence[QuantumTape], Callable):\n    \"\"\"Compile a circuit by applying a series of transforms to a quantum function.\n\n    The default set of transforms includes (in order):\n\n    - pushing all commuting single-qubit gates as far right as possible\n      (:func:`~pennylane.transforms.commute_controlled`)\n    - cancellation of adjacent inverse gates\n      (:func:`~pennylane.transforms.cancel_inverses`)\n    - merging adjacent rotations of the same type\n      (:func:`~pennylane.transforms.merge_rotations`)\n\n    Args:\n        tape (QNode or QuantumTape or Callable): A quantum circuit.\n        pipeline (list[Callable]): A list of\n            tape and/or quantum function transforms to apply.\n        basis_set (list[str]): A list of basis gates. When expanding the tape,\n            expansion will continue until gates in the specific set are\n            reached. If no basis set is specified, a default of\n            ``pennylane.ops.__all__`` will be used. This decomposes templates and\n            operator arithmetic.\n        num_passes (int): The number of times to apply the set of transforms in\n            ``pipeline``. The default is to perform each transform once;\n            however, doing so may produce a new circuit where applying the set\n            of transforms again may yield further improvement, so the number of\n            such passes can be adjusted.\n        expand_depth (int): The depth to use for tape expansion into the basis gates.\n\n    Returns:\n        qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]: The compiled circuit. The output type is explained in :func:`qml.transform <pennylane.transform>`.\n\n    **Example**\n\n    >>> dev = qml.device('default.qubit', wires=[0, 1, 2])\n\n    You can apply the transform directly on a :class:`QNode`:\n\n    .. code-block:: python\n\n        @compile\n        @qml.qnode(device=dev)\n        def circuit(x, y, z):\n            qml.Hadamard(wires=0)\n            qml.Hadamard(wires=1)\n            qml.Hadamard(wires=2)\n            qml.RZ(z, wires=2)\n            qml.CNOT(wires=[2, 1])\n            qml.RX(z, wires=0)\n            qml.CNOT(wires=[1, 0])\n            qml.RX(x, wires=0)\n            qml.CNOT(wires=[1, 0])\n            qml.RZ(-z, wires=2)\n            qml.RX(y, wires=2)\n            qml.PauliY(wires=2)\n            qml.CY(wires=[1, 2])\n            return qml.expval(qml.PauliZ(wires=0))\n\n    The default compilation pipeline is applied before execution.\n\n    Consider the following quantum function:\n\n    .. code-block:: python\n\n        def qfunc(x, y, z):\n            qml.Hadamard(wires=0)\n            qml.Hadamard(wires=1)\n            qml.Hadamard(wires=2)\n            qml.RZ(z, wires=2)\n            qml.CNOT(wires=[2, 1])\n            qml.RX(z, wires=0)\n            qml.CNOT(wires=[1, 0])\n            qml.RX(x, wires=0)\n            qml.CNOT(wires=[1, 0])\n            qml.RZ(-z, wires=2)\n            qml.RX(y, wires=2)\n            qml.PauliY(wires=2)\n            qml.CY(wires=[1, 2])\n            return qml.expval(qml.PauliZ(wires=0))\n\n    Visually, the original function looks like this:\n\n    >>> qnode = qml.QNode(qfunc, dev)\n    >>> print(qml.draw(qnode)(0.2, 0.3, 0.4))\n    0: \u2500\u2500H\u2500\u2500RX(0.40)\u2500\u2500\u2500\u2500\u256dX\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500RX(0.20)\u2500\u256dX\u2500\u2500\u2500\u2500\u2524  <Z>\n    1: \u2500\u2500H\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256dX\u2500\u2570\u25cf\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2570\u25cf\u2500\u256d\u25cf\u2500\u2524\n    2: \u2500\u2500H\u2500\u2500RZ(0.40)\u2500\u2570\u25cf\u2500\u2500RZ(-0.40)\u2500\u2500RX(0.30)\u2500\u2500Y\u2500\u2570Y\u2500\u2524\n\n    We can compile it down to a smaller set of gates using the ``qml.compile``\n    transform.\n\n    >>> compiled_qfunc = qml.compile(qfunc)\n    >>> compiled_qnode = qml.QNode(compiled_qfunc, dev)\n    >>> print(qml.draw(compiled_qnode)(0.2, 0.3, 0.4))\n    0: \u2500\u2500H\u2500\u2500RX(0.60)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524  <Z>\n    1: \u2500\u2500H\u2500\u256dX\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256d\u25cf\u2500\u2500\u2500\u2500\u2524\n    2: \u2500\u2500H\u2500\u2570\u25cf\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500RX(0.30)\u2500\u2570Y\u2500\u2500Y\u2500\u2524\n\n    You can change up the set of transforms by passing a custom ``pipeline`` to\n    ``qml.compile``. The pipeline is a list of transform functions. Furthermore,\n    you can specify a number of passes (repetitions of the pipeline), and a list\n    of gates into which the compiler will first attempt to decompose the\n    existing operations prior to applying any optimization transforms.\n\n    .. code-block:: python3\n\n        compiled_qfunc = qml.compile(\n            pipeline=[\n                partial(qml.transforms.commute_controlled, direction=\"left\"),\n                partial(qml.transforms.merge_rotations, atol=1e-6),\n                qml.transforms.cancel_inverses\n            ],\n            basis_set=[\"CNOT\", \"RX\", \"RY\", \"RZ\"],\n            num_passes=2\n        )(qfunc)\n\n        compiled_qnode = qml.QNode(compiled_qfunc, dev)\n\n        print(qml.draw(compiled_qnode)(0.2, 0.3, 0.4))\n\n    .. code-block::\n\n        0: \u2500\u2500RZ(1.57)\u2500\u2500RX(1.57)\u2500\u2500RZ(1.57)\u2500\u2500RX(0.60)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n        1: \u2500\u2500RZ(1.57)\u2500\u2500RX(1.57)\u2500\u2500RZ(1.57)\u2500\u256dX\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500RZ(1.57)\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256d\u25cf\n        2: \u2500\u2500RZ(1.57)\u2500\u2500RX(1.57)\u2500\u2500RZ(1.57)\u2500\u2570\u25cf\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500RX(0.30)\u2500\u2500RZ(1.57)\u2500\u2500RY(3.14)\u2500\u2500RZ(1.57)\u2500\u2500RY(1.57)\u2500\u2570X\n\n        \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524  <Z>\n        \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256d\u25cf\u2500\u2524\n        \u2500\u2500\u2500RY(-1.57)\u2500\u2570X\u2500\u2524\n\n    \"\"\"\n    # Ensure that everything in the pipeline is a valid qfunc or tape transform\n    if pipeline is None:\n        pipeline = default_pipeline\n    else:\n        for p in pipeline:\n            p_func = p.func if isinstance(p, partial) else p\n            if not isinstance(p_func, TransformDispatcher):\n                raise ValueError(\"Invalid transform function {p} passed to compile.\")\n\n    if num_passes < 1 or not isinstance(num_passes, int):\n        raise ValueError(\"Number of passes must be an integer with value at least 1.\")\n\n    # Expand the tape; this is done to unroll any templates that may be present,\n    # as well as to decompose over a specified basis set\n    # First, though, we have to stop whatever tape may be recording so that we\n    # don't queue anything as a result of the expansion or transform pipeline\n\n    with QueuingManager.stop_recording():\n        basis_set = basis_set or all_ops\n\n        def stop_at(obj):\n            return obj.name in basis_set and (not getattr(obj, \"only_visual\", False))\n\n        expanded_tape = tape.expand(depth=expand_depth, stop_at=stop_at)\n\n        # Apply the full set of compilation transforms num_passes times\n        for _ in range(num_passes):\n            for transf in pipeline:\n                tapes, _ = transf(expanded_tape)\n                expanded_tape = tapes[0]\n\n    new_tape = type(tape)(\n        expanded_tape.operations, expanded_tape.measurements, shots=expanded_tape.shots\n    )\n\n    def null_postprocessing(results):\n        \"\"\"A postprocesing function returned by a transform that only converts the batch of results\n        into a result for a single ``QuantumTape``.\n        \"\"\"\n        return results[0]\n\n    return [new_tape], null_postprocessing\n", "path": "pennylane/transforms/compile.py"}]}