Source code for pennylane.transforms.decompositions.pauli_based_computation

# Copyright 2025 Xanadu Quantum Technologies Inc.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Aliases for pauli-based computation passes from Catalyst's passes module."""

from functools import partial

from pennylane.transforms.core import transform


[docs] @partial(transform, pass_name="to-ppr") def to_ppr(tape): r"""A quantum compilation pass that converts Clifford+T gates into Pauli Product Rotation (PPR) gates. .. note:: This transform requires decorating the workflow with :func:`@qml.qjit <pennylane.qjit>`. In addition, the circuits generated by this pass are currently not executable on any backend. This pass is only for Pauli-based-computation analysis with the ``null.qubit`` device and potential future execution when a suitable backend is available. Clifford gates are defined as :math:`\exp(-{iP\tfrac{\pi}{4}})`, where :math:`P` is a Pauli word. Non-Clifford gates are defined as :math:`\exp(-{iP\tfrac{\pi}{8}})`. For more information on Pauli product measurements and Pauli product rotations, check out the `compilation hub <https://pennylane.ai/compilation/pauli-product-measurement>`__. The full list of supported gates and operations are ``qml.H``, ``qml.S``, ``qml.T``, ``qml.X``, ``qml.Y``, ``qml.Z``, ``qml.PauliRot``, ``qml.adjoint(qml.PauliRot)``, ``qml.adjoint(qml.S)``, ``qml.adjoint(qml.T)``, ``qml.CNOT``, and ``qml.measure``. Args: fn (QNode): the QNode to apply the pass to Returns: :class:`QNode <pennylane.QNode>` .. seealso:: :func:`~.transforms.commute_ppr`, :func:`~.transforms.merge_ppr_ppm`, :func:`~.transforms.ppr_to_ppm`, :func:`~.transforms.ppm_compilation`, :func:`~.transforms.reduce_t_depth`, :func:`~.transforms.decompose_arbitrary_ppr` .. note:: For better compatibility with other PennyLane functionality, ensure that PennyLane program capture is enabled with :func:`pennylane.capture.enable`. **Example** The ``to_ppr`` compilation pass can be applied as a decorator on a QNode: .. code-block:: python import pennylane as qml qml.capture.enable() @qml.qjit(target="mlir") @qml.transforms.to_ppr @qml.qnode(qml.device("null.qubit", wires=2)) def circuit(): qml.H(0) qml.CNOT([0, 1]) m = qml.measure(0) qml.T(0) return qml.expval(qml.Z(0)) To inspect programs compiled with ``to_ppr`` via :func:`~.specs`, ensure that ``target="mlir"`` is given in the :func:`qjit <pennylane.qjit>` decorator. >>> print(qml.specs(circuit, level=2)()) Device: null.qubit Device wires: 2 Shots: Shots(total=None) Level: 2 <BLANKLINE> Resource specifications: Total wire allocations: 2 Total gates: 8 Circuit depth: Not computed <BLANKLINE> Gate types: PPR-pi/4: 6 PPM: 1 PPR-pi/8: 1 <BLANKLINE> Measurements: expval(PauliZ): 1 In the above output, ``PPR-theta`` denotes the type of PPR present in the circuit, where ``theta`` is the PPR angle (:math:`\theta`). Note that the mid-circuit measurement (:func:`pennylane.measure`) in the circuit has been converted to a Pauli product measurement (PPM), as well. """ raise NotImplementedError( "The 'to_ppr' compilation pass has no tape implementation, " "and can only be applied when decorating the entire worfklow " "with '@qml.qjit' and when it is placed after all transforms " "that only have a tape implementation." )
[docs] @partial(transform, pass_name="commute-ppr") def commute_ppr(tape, *, max_pauli_size=0): r"""A quantum compilation pass that commutes Clifford Pauli product rotation (PPR) gates, :math:`\exp(-{iP\tfrac{\pi}{4}})`, past non-Clifford PPRs gates, :math:`\exp(-{iP\tfrac{\pi}{8}})`, where :math:`P` is a Pauli word. .. note:: This transform requires decorating the workflow with :func:`@qml.qjit <pennylane.qjit>`. In addition, the circuits generated by this pass are currently not executable on any backend. This pass is only for Pauli-based-computation analysis with the ``null.qubit`` device and potential future execution when a suitable backend is available. Lastly, the :func:`pennylane.transforms.to_ppr` transform must be applied before ``commute_ppr``. For more information on PPRs, check out the `Compilation Hub <https://pennylane.ai/compilation/pauli-product-measurement>`_. Args: fn (QNode): QNode to apply the pass to. max_pauli_size (int): The maximum size of Pauli strings resulting from commutation. If a commutation results in a PPR that acts on more than ``max_pauli_size`` qubits, that commutation will not be performed. Note that the default ``max_pauli_size=0`` indicates no limit. Returns: :class:`QNode <pennylane.QNode>` .. seealso:: :func:`~.transforms.to_ppr`, :func:`~.transforms.merge_ppr_ppm`, :func:`~.transforms.ppr_to_ppm`, :func:`~.transforms.ppm_compilation`, :func:`~.transforms.reduce_t_depth`, :func:`~.transforms.decompose_arbitrary_ppr` .. note:: For better compatibility with other PennyLane functionality, ensure that PennyLane program capture is enabled with :func:`pennylane.capture.enable`. **Example** The ``commute_ppr`` compilation pass can be applied as a decorator on a QNode: .. code-block:: python import pennylane as qml import jax.numpy as jnp qml.capture.enable() @qml.qjit(target="mlir") @qml.transforms.commute_ppr(max_pauli_size=2) @qml.transforms.to_ppr @qml.qnode(qml.device("null.qubit", wires=2)) def circuit(): # equivalent to a Hadamard gate qml.PauliRot(jnp.pi / 2, pauli_word="Z", wires=0) qml.PauliRot(jnp.pi / 2, pauli_word="X", wires=0) qml.PauliRot(jnp.pi / 2, pauli_word="Z", wires=0) # equivalent to a CNOT gate qml.PauliRot(jnp.pi / 2, pauli_word="ZX", wires=[0, 1]) qml.PauliRot(-jnp.pi / 2, pauli_word="Z", wires=0) qml.PauliRot(-jnp.pi / 2, pauli_word="X", wires=1) # equivalent to a T gate qml.PauliRot(jnp.pi / 4, pauli_word="Z", wires=0) return qml.expval(qml.Z(0)) To inspect programs compiled with ``commute_ppr`` via :func:`~.specs`, ensure that ``target="mlir"`` is given in the :func:`qjit <pennylane.qjit>` decorator. >>> print(qml.specs(circuit, level=3)()) Device: null.qubit Device wires: 2 Shots: Shots(total=None) Level: 3 <BLANKLINE> Resource specifications: Total wire allocations: 2 Total gates: 7 Circuit depth: Not computed <BLANKLINE> Gate types: PPR-pi/8: 1 PPR-pi/4: 6 <BLANKLINE> Measurements: expval(PauliZ): 1 In the example above, the Clifford PPRs (:class:`~.PauliRot` instances with an angle of rotation of :math:`\tfrac{\pi}{2}`) will be commuted past the non-Clifford PPR (:class:`~.PauliRot` instances with an angle of rotation of :math:`\tfrac{\pi}{4}`). In the output above, ``PPR-theta`` denotes the type of PPR present in the circuit, where ``theta`` is the PPR angle (:math:`\theta`). Note that if a commutation resulted in a PPR acting on more than ``max_pauli_size`` qubits (here, ``max_pauli_size = 2``), that commutation would be skipped. """ raise NotImplementedError( "The 'commute_ppr' compilation pass has no tape implementation, " "and can only be applied when decorating the entire worfklow " "with '@qml.qjit' and when it is placed after all transforms " "that only have a tape implementation." )
[docs] @partial(transform, pass_name="merge-ppr-ppm") def merge_ppr_ppm(tape=None, *, max_pauli_size=0): r""" A quantum compilation pass that absorbs Clifford Pauli product rotation (PPR) operations, :math:`\exp{-iP\tfrac{\pi}{4}}`, into the final Pauli product measurements (PPMs). .. note:: This transform requires decorating the workflow with :func:`@qml.qjit <pennylane.qjit>`. In addition, the circuits generated by this pass are currently not executable on any backend. This pass is only for Pauli-based-computation analysis with the ``null.qubit`` device and potential future execution when a suitable backend is available. Secondly, the ``merge_ppr_ppm`` transform does not currently affect terminal measurements. So, for accurate results, it is recommended to return nothing (i.e., a blank ``return`` statement) from the QNode. Lastly, the :func:`pennylane.transforms.to_ppr` transform must be applied before ``merge_ppr_ppm``. For more information on PPRs and PPMs, check out the `Compilation Hub <https://pennylane.ai/compilation/pauli-product-measurement>`_. Args: fn (QNode): QNode to apply the pass to max_pauli_size (int): The maximum size of Pauli strings resulting from merging. If a merge results in a PPM that acts on more than ``max_pauli_size`` qubits, that merge will not be performed. The default value is ``0`` (no limit). Returns: :class:`QNode <pennylane.QNode>` .. seealso:: :func:`~.transforms.to_ppr`, :func:`~.transforms.commute_ppr`, :func:`~.transforms.ppr_to_ppm`, :func:`~.transforms.ppm_compilation`, :func:`~.transforms.reduce_t_depth`, :func:`~.transforms.decompose_arbitrary_ppr` .. note:: For better compatibility with other PennyLane functionality, ensure that PennyLane program capture is enabled with :func:`pennylane.capture.enable`. **Example** The ``merge_ppr_ppm`` compilation pass can be applied as a decorator on a QNode: .. code-block:: python import pennylane as qml import jax.numpy as jnp qml.capture.enable() @qml.qjit(target="mlir") @qml.transforms.merge_ppr_ppm(max_pauli_size=2) @qml.transforms.to_ppr @qml.qnode(qml.device("null.qubit", wires=2)) def circuit(): qml.PauliRot(jnp.pi / 2, pauli_word="Z", wires=0) qml.PauliRot(jnp.pi / 2, pauli_word="X", wires=1) ppm = qml.pauli_measure(pauli_word="ZX", wires=[0, 1]) return To inspect programs compiled with ``merge_ppr_ppm`` via :func:`~.specs`, ensure that ``target="mlir"`` is given in the :func:`qjit <pennylane.qjit>` decorator. >>> print(qml.specs(circuit, level=3)()) Device: null.qubit Device wires: 2 Shots: Shots(total=None) Level: 3 <BLANKLINE> Resource specifications: Total wire allocations: 2 Total gates: 1 Circuit depth: Not computed <BLANKLINE> Gate types: PPM: 1 <BLANKLINE> Measurements: No measurements. If a merging resulted in a PPM acting on more than ``max_pauli_size`` qubits, that merging operation would be skipped. """ raise NotImplementedError( "The 'merge_ppr_ppm' compilation pass has no tape implementation, " "and can only be applied when decorating the entire worfklow " "with '@qml.qjit' and when it is placed after all transforms " "that only have a tape implementation." )
[docs] @partial(transform, pass_name="ppr-to-ppm") def ppr_to_ppm(tape=None, *, decompose_method="pauli-corrected", avoid_y_measure=False): r""" A quantum compilation pass that decomposes Pauli product rotations (PPRs), :math:`P(\theta) = \exp(-iP\theta)`, into Pauli product measurements (PPMs). .. note:: This transform requires decorating the workflow with :func:`@qml.qjit <pennylane.qjit>`. In addition, the circuits generated by this pass are currently not executable on any backend. This pass is only for Pauli-based-computation analysis with the ``null.qubit`` device and potential future execution when a suitable backend is available. Lastly, the :func:`pennylane.transforms.to_ppr` transform must be applied before ``ppr_to_ppm``. This pass is used to decompose both non-Clifford and Clifford PPRs into PPMs. The non-Clifford PPRs (:math:`\theta = \tfrac{\pi}{8}`) are decomposed first, then Clifford PPRs (:math:`\theta = \tfrac{\pi}{4}`) are decomposed. For more information on PPRs and PPMs, check out the `Compilation Hub <https://pennylane.ai/compilation/pauli-product-measurement>`_. Args: qnode (QNode): QNode to apply the pass to. decompose_method (str): The method to use for decomposing non-Clifford PPRs. Options are ``"pauli-corrected"``, ``"auto-corrected"``, and ``"clifford-corrected"``. Defaults to ``"pauli-corrected"``. ``"pauli-corrected"`` uses a reactive measurement for correction that is based on Figure 13 in `arXiv:2211.15465 <https://arxiv.org/pdf/2211.15465>`_. ``"auto-corrected"`` uses an additional measurement for correction that is based on Figure 7 in `A Game of Surface Codes <https://arxiv.org/abs/1808.02892>`__, and ``"clifford-corrected"`` uses a Clifford rotation for correction that is based on Figure 17(b) in `A Game of Surface Codes <https://arxiv.org/abs/1808.02892>`__. avoid_y_measure (bool): Rather than performing a Pauli-Y measurement for Clifford rotations (sometimes more costly), a :math:`Y` state (:math:`Y\vert 0 \rangle`) is used instead (requires :math:`Y`-state preparation). This is currently only supported when using the ``"clifford-corrected"`` and ``"pauli-corrected"`` decomposition methods. Defaults to ``False``. Returns: :class:`QNode <pennylane.QNode>` .. seealso:: :func:`~.transforms.to_ppr`, :func:`~.transforms.commute_ppr`, :func:`~.transforms.merge_ppr_ppm`, :func:`~.transforms.ppm_compilation`, :func:`~.transforms.reduce_t_depth`, :func:`~.transforms.decompose_arbitrary_ppr` .. note:: For better compatibility with other PennyLane functionality, ensure that PennyLane program capture is enabled with :func:`pennylane.capture.enable`. **Example** The ``ppr_to_ppm`` compilation pass can be applied as a decorator on a QNode: .. code-block:: python import pennylane as qml from functools import partial import jax.numpy as jnp qml.capture.enable() @qml.qjit(target="mlir") @qml.transforms.ppr_to_ppm(decompose_method="auto-corrected") @qml.transforms.to_ppr @qml.qnode(qml.device("null.qubit", wires=2)) def circuit(): # equivalent to a Hadamard gate qml.PauliRot(jnp.pi / 2, pauli_word="Z", wires=0) qml.PauliRot(jnp.pi / 2, pauli_word="X", wires=0) qml.PauliRot(jnp.pi / 2, pauli_word="Z", wires=0) # equivalent to a CNOT gate qml.PauliRot(jnp.pi / 2, pauli_word="ZX", wires=[0, 1]) qml.PauliRot(-jnp.pi / 2, pauli_word="Z", wires=[0]) qml.PauliRot(-jnp.pi / 2, pauli_word="X", wires=[1]) # equivalent to a T gate qml.PauliRot(jnp.pi / 4, pauli_word="Z", wires=0) return qml.expval(qml.Z(0)) To inspect programs compiled with ``merge_ppr_ppm`` via :func:`~.specs`, ensure that ``target="mlir"`` is given in the :func:`qjit <pennylane.qjit>` decorator. >>> print(qml.specs(circuit, level=3)()) Device: null.qubit Device wires: 2 Shots: Shots(total=None) Level: 3 <BLANKLINE> Resource specifications: Total wire allocations: 9 Total gates: 24 Circuit depth: Not computed <BLANKLINE> Gate types: PPM: 16 PPR-pi/2: 7 qec.fabricate: 1 <BLANKLINE> Measurements: expval(PauliZ): 1 In the above output, ``PPR-theta`` denotes the type of PPR present in the circuit, where ``theta`` is the PPR angle (:math:`\theta`). Note that :math:`\theta = \tfrac{\pi}{2}` PPRs correspond to Pauli operators (:math:`P(\tfrac{\pi}{2}) = \exp(-iP\tfrac{\pi}{2}) = P`). Pauli operators can be commuted to the end of the circuit and absorbed into terminal measurements. """ raise NotImplementedError( "The 'ppr_to_ppm' compilation pass has no tape implementation, " "and can only be applied when decorating the entire worfklow " "with '@qml.qjit' and when it is placed after all transforms " "that only have a tape implementation." )
[docs] @partial(transform, pass_name="ppm-compilation") def ppm_compilation( tape=None, *, decompose_method="pauli-corrected", avoid_y_measure=False, max_pauli_size=0 ): r""" A quantum compilation pass that transforms Clifford+T gates into Pauli product measurements (PPMs). .. note:: This transform requires decorating the workflow with :func:`@qml.qjit <pennylane.qjit>`. In addition, the circuits generated by this pass are currently not executable on any backend. This pass is only for Pauli-based-computation analysis with the ``null.qubit`` device and potential future execution when a suitable backend is available. This pass combines multiple sub-passes: - :func:`~.transforms.to_ppr` : Converts gates into Pauli Product Rotations (PPRs) - :func:`~.transforms.commute_ppr` : Commutes PPRs past non-Clifford PPRs - :func:`~.transforms.merge_ppr_ppm` : Merges PPRs into Pauli Product Measurements (PPMs) - :func:`~.transforms.ppr_to_ppm` : Decomposes PPRs into PPMs The ``avoid_y_measure`` and ``decompose_method`` arguments are passed to the :func:`~.transforms.ppr_to_ppm` pass. The ``max_pauli_size`` argument is passed to the :func:`~.transforms.commute_ppr` and :func:`~.transforms.merge_ppr_ppm` passes. For more information on PPRs and PPMs, check out the `Compilation Hub <https://pennylane.ai/compilation/pauli-product-measurement>`_. Args: qnode (QNode, optional): QNode to apply the pass to. If ``None``, returns a decorator. decompose_method (str, optional): The method to use for decomposing non-Clifford PPRs. Options are ``"pauli-corrected"``, ``"auto-corrected"``, and ``"clifford-corrected"``. Defaults to ``"pauli-corrected"``. ``"pauli-corrected"`` uses a reactive measurement for correction that is based on Figure 13 in `arXiv:2211.15465 <https://arxiv.org/pdf/2211.15465>`_. ``"auto-corrected"`` uses an additional measurement for correction that is based on Figure 7 in `A Game of Surface Codes <https://arxiv.org/abs/1808.02892>`__, and ``"clifford-corrected"`` uses a Clifford rotation for correction that is based on Figure 17(b) in `A Game of Surface Codes <https://arxiv.org/abs/1808.02892>`__. avoid_y_measure (bool): Rather than performing a Pauli-Y measurement for Clifford rotations (sometimes more costly), a :math:`Y` state (:math:`Y\vert 0 \rangle`) is used instead (requires :math:`Y`-state preparation). This is currently only supported when using the ``"clifford-corrected"`` and ``"pauli-corrected"`` decomposition methods. Defaults to ``False``. max_pauli_size (int): The maximum size of the Pauli strings after commuting or merging. Defaults to 0 (no limit). Returns: :class:`QNode <pennylane.QNode>` .. note:: For better compatibility with other PennyLane functionality, ensure that PennyLane program capture is enabled with :func:`pennylane.capture.enable`. **Example** The ``ppm_compilation`` compilation pass can be applied as a decorator on a QNode: .. code-block:: python import pennylane as qml qml.capture.enable() @qml.qjit(target="mlir") @qml.transforms.ppm_compilation(decompose_method="clifford-corrected", max_pauli_size=2) @qml.qnode(qml.device("null.qubit", wires=2)) def circuit(): qml.H(0) qml.CNOT([0, 1]) qml.T(0) return qml.expval(qml.Z(0)) To inspect programs compiled with ``ppm_compilation`` via :func:`~.specs`, ensure that ``target="mlir"`` is given in the :func:`qjit <pennylane.qjit>` decorator. >>> print(qml.specs(circuit, level=2)()) Device: null.qubit Device wires: 2 Shots: Shots(total=None) Level: 2 <BLANKLINE> Resource specifications: Total wire allocations: 8 Total gates: 22 Circuit depth: Not computed <BLANKLINE> Gate types: qec.fabricate: 1 PPM: 14 PPR-pi/2: 7 <BLANKLINE> Measurements: expval(PauliZ): 1 In the above output, ``PPR-theta`` denotes the type of PPR present in the circuit, where ``theta`` is the PPR angle (:math:`\theta`). Note that :math:`\theta = \tfrac{\pi}{2}` PPRs correspond to Pauli operators (:math:`P(\tfrac{\pi}{2}) = \exp(-iP\tfrac{\pi}{2}) = P`). Pauli operators can be commuted to the end of the circuit and absorbed into terminal measurements. Note that if a commutation or merge resulted in a PPR or PPM acting on more than ``max_pauli_size`` qubits (here, ``max_pauli_size = 2``), that commutation or merge would be skipped. """ raise NotImplementedError( "The 'ppm_compilation' compilation pass has no tape implementation, " "and can only be applied when decorating the entire worfklow " "with '@qml.qjit' and when it is placed after all transforms " "that only have a tape implementation." )
[docs] @partial(transform, pass_name="reduce-t-depth") def reduce_t_depth(qnode): r""" A quantum compilation pass that reduces the depth and count of non-Clifford Pauli product rotation (PPR, :math:`P(\theta) = \exp(-iP\theta)`) operators (e.g., ``T`` gates) by commuting PPRs in adjacent layers and merging compatible ones (a layer comprises PPRs that mutually commute). For more details, see Figure 6 of `A Game of Surface Codes <https://arXiv:1808.02892v3>`_. .. note:: This transform requires decorating the workflow with :func:`@qml.qjit <pennylane.qjit>`. In addition, the circuits generated by this pass are currently not executable on any backend. This pass is only for Pauli-based-computation analysis with the ``null.qubit`` device and potential future execution when a suitable backend is available. Lastly, the :func:`pennylane.transforms.to_ppr` transform must be applied before ``reduce_t_depth``. Args: qnode (QNode): QNode to apply the pass to. Returns: ~.QNode: Returns decorated QNode. .. seealso:: :func:`~.transforms.to_ppr`, :func:`~.transforms.commute_ppr`, :func:`~.transforms.merge_ppr_ppm`, :func:`~.transforms.ppr_to_ppm`, :func:`~.transforms.ppm_compilation`, :func:`~.transforms.decompose_arbitrary_ppr` .. note:: For better compatibility with other PennyLane functionality, ensure that PennyLane program capture is enabled with :func:`pennylane.capture.enable`. **Example** In the example below, after performing the :func:`~.transforms.to_ppr` and :func:`~.transforms.merge_ppr_ppm` passes, the circuit contains a depth of four of non-Clifford PPRs. Subsequently applying the ``reduce_t_depth`` pass will move PPRs around via commutation, resulting in a circuit with a smaller PPR depth. Specifically, in the circuit below, the Pauli-:math:`X` PPR (:math:`\exp(iX\tfrac{\pi}{8})`) on qubit Q1 will be moved to the first layer, which results in a depth of three non-Clifford PPRs. Consider the following example: .. code-block:: python import pennylane as qml import jax.numpy as jnp qml.capture.enable() @qml.qjit(target="mlir") @qml.transforms.reduce_t_depth @qml.transforms.to_ppr @qml.qnode(qml.device("null.qubit", wires=4)) def circuit(): qml.PauliRot(jnp.pi / 4, pauli_word="Z", wires=1) qml.PauliRot(-jnp.pi / 4, pauli_word="XYZ", wires=[0, 2, 3]) qml.PauliRot(-jnp.pi / 2, pauli_word="XYZY", wires=[0, 1, 2, 3]) qml.PauliRot(jnp.pi / 4, pauli_word="XZX", wires=[0, 1, 3]) qml.PauliRot(-jnp.pi / 4, pauli_word="XZY", wires=[0, 1, 2]) return qml.expval(qml.Z(0)) The ``reduce_t_depth`` compilation pass will rearrange the last three PPRs in the above circuit to reduce the non-Clifford PPR depth. This is best seen with the :func:`catalyst.draw_graph` function: >>> import catalyst >>> num_passes = 2 >>> fig1, _ = catalyst.draw_graph(circuit, level=num_passes-1)() # doctest: +SKIP >>> fig2, _ = catalyst.draw_graph(circuit, level=num_passes)() # doctest: +SKIP Without ``reduce_t_depth`` applied: >>> fig1.savefig('path_to_file1.png', dpi=300, bbox_inches="tight") # doctest: +SKIP .. figure:: ../../../doc/_static/reduce-t-depth-example1.png :width: 35% :alt: Graphical representation of circuit without ``reduce_t_depth`` :align: left With ``reduce_t_depth`` applied: >>> fig2.savefig('path_to_file2.png', dpi=300, bbox_inches="tight") # doctest: +SKIP .. figure:: ../../../doc/_static/reduce-t-depth-example2.png :width: 35% :alt: Graphical representation of circuit with ``reduce_t_depth`` :align: left """ raise NotImplementedError( "The 'reduce_t_depth' compilation pass has no tape implementation, " "and can only be applied when decorating the entire worfklow " "with '@qml.qjit' and when it is placed after all transforms " "that only have a tape implementation." )
[docs] @partial(transform, pass_name="decompose-arbitrary-ppr") def decompose_arbitrary_ppr(qnode): r""" A quantum compilation pass that decomposes arbitrary-angle Pauli product rotations (PPRs) into a collection of PPRs (with angles of rotation of :math:`\tfrac{\pi}{2}`, :math:`\tfrac{\pi}{4}`, and :math:`\tfrac{\pi}{8}`), PPMs and a single-qubit arbitrary-angle PPR in the Z basis. For details, see `Figure 13(d) of arXiv:2211.15465 <https://arxiv.org/abs/2211.15465>`__. .. note:: This transform requires decorating the workflow with :func:`@qml.qjit <pennylane.qjit>`. In addition, the circuits generated by this pass are currently not executable on any backend. This pass is only for Pauli-based-computation analysis with the ``null.qubit`` device and potential future execution when a suitable backend is available. Lastly, the :func:`pennylane.transforms.to_ppr` transform must be applied before ``decompose_arbitrary_ppr``. Args: qnode (QNode): QNode to apply the pass to. Returns: ~.QNode: Returns decorated QNode. .. seealso:: :func:`~.transforms.to_ppr`, :func:`~.transforms.commute_ppr`, :func:`~.transforms.merge_ppr_ppm`, :func:`~.transforms.ppr_to_ppm`, :func:`~.transforms.ppm_compilation`, :func:`~.transforms.reduce_t_depth` .. note:: For better compatibility with other PennyLane functionality, ensure that PennyLane program capture is enabled with :func:`pennylane.capture.enable`. **Example** In the example below, the arbitrary-angle PPR (``qml.PauliRot(0.1, pauli_word="XY", wires=[0, 1])``), will be decomposed into various other PPRs and PPMs in accordance with `Figure 13(d) of arXiv:2211.15465 <https://arxiv.org/abs/2211.15465>`__. .. code-block:: python import pennylane as qml qml.capture.enable() @qml.qjit(target="mlir") @qml.transforms.decompose_arbitrary_ppr @qml.transforms.to_ppr @qml.qnode(qml.device("null.qubit", wires=3)) def circuit(): qml.PauliRot(0.1, pauli_word="XY", wires=[0, 1]) return qml.expval(qml.Z(0)) >>> print(qml.specs(circuit, level=3)()) Device: null.qubit Device wires: 3 Shots: Shots(total=None) Level: 3 <BLANKLINE> Resource specifications: Total wire allocations: 4 Total gates: 6 Circuit depth: Not computed <BLANKLINE> Gate types: qec.prepare: 1 PPM: 2 PPR-pi/2: 2 PPR-Phi: 1 <BLANKLINE> Measurements: expval(PauliZ): 1 In the above output, ``PPR-theta`` denotes the type of PPR present in the circuit, where ``theta`` is the PPR angle (:math:`\theta`). ``PPR-Phi`` corresponds to a PPR whose angle of rotation is not :math:`\tfrac{\pi}{2}`, :math:`\tfrac{\pi}{4}`, or :math:`\tfrac{\pi}{8}`. """ raise NotImplementedError( "The 'decompose_arbitrary_ppr' compilation pass has no tape " "implementation, and can only be applied when decorating the " "entire workflow with '@qml.qjit' and when it is placed after " "all transforms that only have a tape implementation." )