Repository: PennyLaneAI/pennylane
Pull request #7526 (2025-05-29 13:58:05)

Added controlled resource operators
**Context:**
Added controlled ResourceOperator classes

**Description of the Change:**

**Benefits:**

**Possible Drawbacks:**

**Related GitHub Issues:**

[sc-91020]

github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [`doc/releases/changelog-dev.md`](/PennyLaneAI/pennylane/blob/master/doc/releases/changelog-dev.md) with:
* A one-to-two sentence description of the change. You may include a small working example for new features.
* A link back to this PR.
* Your name (or GitHub username) in the contributors section.

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 6ad864bd1..42733d711 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -579,6 +579,10 @@
   estimation for parametric single qubit gates.
   [(#7541)](https://github.com/PennyLaneAI/pennylane/pull/7541)
 
+* Added the `pennylane.labs.ResourceOperator` templates which will be used to perform resource
+  estimation for controlled gates.
+  [(#7526)](https://github.com/PennyLaneAI/pennylane/pull/7526)
+
 * A new module :mod:`pennylane.labs.zxopt <pennylane.labs.zxopt>` provides access to the basic optimization
   passes from [pyzx](https://pyzx.readthedocs.io/en/latest/) for PennyLane circuits.
   
diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py
index 10acbb67b..d2a2df917 100644
--- a/pennylane/labs/resource_estimation/__init__.py
+++ b/pennylane/labs/resource_estimation/__init__.py
@@ -76,6 +76,21 @@ Operators:
     ~ResourceRZ
     ~ResourceRot
     ~ResourcePhaseShift
+    ~ResourceSWAP
+    ~ResourceCH
+    ~ResourceCY
+    ~ResourceCZ
+    ~ResourceCSWAP
+    ~ResourceCCZ
+    ~ResourceCNOT
+    ~ResourceToffoli
+    ~ResourceMultiControlledX
+    ~ResourceCRX
+    ~ResourceCRY
+    ~ResourceCRZ
+    ~ResourceCRot
+    ~ResourceControlledPhaseShift
+    ~ResourceTempAND
 
 """
 
@@ -100,17 +115,32 @@ from .resource_tracking import (
     estimate_resources,
 )
 from .ops import (
-    ResourceGlobalPhase,
     ResourceHadamard,
-    ResourceIdentity,
-    ResourcePhaseShift,
-    ResourceRot,
-    ResourceRX,
-    ResourceRY,
-    ResourceRZ,
     ResourceS,
-    ResourceT,
     ResourceX,
     ResourceY,
     ResourceZ,
+    ResourceRX,
+    ResourceRY,
+    ResourceRZ,
+    ResourceT,
+    ResourcePhaseShift,
+    ResourceGlobalPhase,
+    ResourceRot,
+    ResourceIdentity,
+    ResourceSWAP,
+    ResourceCH,
+    ResourceCY,
+    ResourceCZ,
+    ResourceCSWAP,
+    ResourceCCZ,
+    ResourceCNOT,
+    ResourceToffoli,
+    ResourceMultiControlledX,
+    ResourceCRX,
+    ResourceCRY,
+    ResourceCRZ,
+    ResourceCRot,
+    ResourceControlledPhaseShift,
+    ResourceTempAND,
 )
diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py
index 869a5a97f..3b1395ced 100644
--- a/pennylane/labs/resource_estimation/ops/__init__.py
+++ b/pennylane/labs/resource_estimation/ops/__init__.py
@@ -14,6 +14,24 @@
 r"""This module contains resource operators for PennyLane Operators"""
 
 from .identity import ResourceGlobalPhase, ResourceIdentity
+
+from .op_math import (
+    ResourceCCZ,
+    ResourceCH,
+    ResourceCNOT,
+    ResourceControlledPhaseShift,
+    ResourceCRot,
+    ResourceCRX,
+    ResourceCRY,
+    ResourceCRZ,
+    ResourceCSWAP,
+    ResourceCY,
+    ResourceCZ,
+    ResourceMultiControlledX,
+    ResourceTempAND,
+    ResourceToffoli,
+)
+
 from .qubit import (
     ResourceHadamard,
     ResourcePhaseShift,
@@ -22,6 +40,7 @@ from .qubit import (
     ResourceRY,
     ResourceRZ,
     ResourceS,
+    ResourceSWAP,
     ResourceT,
     ResourceX,
     ResourceY,
diff --git a/pennylane/labs/resource_estimation/ops/op_math/__init__.py b/pennylane/labs/resource_estimation/ops/op_math/__init__.py
new file mode 100644
index 000000000..92bace670
--- /dev/null
+++ b/pennylane/labs/resource_estimation/ops/op_math/__init__.py
@@ -0,0 +1,31 @@
+# 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.
+r"""This module contains experimental resource estimation functionality."""
+
+from .controlled_ops import (
+    ResourceCCZ,
+    ResourceCH,
+    ResourceCNOT,
+    ResourceControlledPhaseShift,
+    ResourceCRot,
+    ResourceCRX,
+    ResourceCRY,
+    ResourceCRZ,
+    ResourceCSWAP,
+    ResourceCY,
+    ResourceCZ,
+    ResourceMultiControlledX,
+    ResourceTempAND,
+    ResourceToffoli,
+)
diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py
new file mode 100644
index 000000000..24b7d1e8c
--- /dev/null
+++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py
@@ -0,0 +1,1705 @@
+# 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.
+r"""Resource operators for controlled operations."""
+
+import pennylane.labs.resource_estimation as re
+from pennylane.labs.resource_estimation.qubit_manager import AllocWires, FreeWires
+from pennylane.labs.resource_estimation.resource_operator import (
+    CompressedResourceOp,
+    GateCount,
+    ResourceOperator,
+    resource_rep,
+)
+
+# pylint: disable=arguments-differ,too-many-ancestors,too-many-arguments,too-many-positional-arguments
+
+
+class ResourceCH(ResourceOperator):
+    r"""Resource class for the CH gate.
+
+    Args:
+        wires (Sequence[int], optional): the wires the operation acts on
+
+    Resources:
+        The resources are derived from the following identities (as presented in this
+        `blog post <https://quantumcomputing.stackexchange.com/questions/15734/how-to-construct-a-controlled-hadamard-gate-using-single-qubit-gates-and-control>`_):
+
+        .. math::
+
+            \begin{align}
+                \hat{H} &= \hat{R}_{y}(\frac{\pi}{4}) \cdot \hat{Z}  \cdot \hat{R}_{y}(\frac{-\pi}{4}), \\
+                \hat{Z} &= \hat{H} \cdot \hat{X}  \cdot \hat{H}.
+            \end{align}
+
+        Specifically, the resources are defined as two :class:`~.ResourceRY`, two
+        :class:`~.ResourceHadamard` and one :class:`~.ResourceCNOT` gates.
+
+    .. seealso:: :class:`~.CH`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceCH.resource_decomp()
+    [(2 x Hadamard), (2 x RY), (1 x CNOT)]
+
+    """
+
+    num_wires = 2
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            dict: Empty dictionary. The resources of this operation don't depend on any additional parameters.
+        """
+        return {}
+
+    @classmethod
+    def resource_rep(cls) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+        return CompressedResourceOp(cls, {})
+
+    @classmethod
+    def default_resource_decomp(cls, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            The resources are derived from the following identities (as presented in this
+            `blog post <https://quantumcomputing.stackexchange.com/questions/15734/how-to-construct-a-controlled-hadamard-gate-using-single-qubit-gates-and-control>`_):
+
+            .. math::
+
+                \begin{align}
+                    \hat{H} &= \hat{R}_{y}(\frac{\pi}{4}) \cdot \hat{Z}  \cdot \hat{R}_{y}(\frac{-\pi}{4}), \\
+                    \hat{Z} &= \hat{H} \cdot \hat{X}  \cdot \hat{H}.
+                \end{align}
+
+            Specifically, the resources are defined as two :class:`~.ResourceRY`, two
+            :class:`~.ResourceHadamard` and one :class:`~.ResourceCNOT` gates.
+        """
+        ry = resource_rep(re.ResourceRY)
+        h = resource_rep(re.ResourceHadamard)
+        cnot = resource_rep(ResourceCNOT)
+        return [GateCount(h, 2), GateCount(ry, 2), GateCount(cnot, 1)]
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            This operation is self-adjoint, so the resources of the adjoint operation results
+            in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep())]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+
+        Resources:
+            This operation is self-inverse, thus when raised to even integer powers acts like
+            the identity operator and raised to odd powers it produces itself.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return (
+            [GateCount(resource_rep(re.ResourceIdentity))]
+            if pow_z % 2 == 0
+            else [GateCount(cls.resource_rep())]
+        )
+
+
+class ResourceCY(ResourceOperator):
+    r"""Resource class for the CY gate.
+
+    Args:
+        wires (Sequence[int], optional): the wires the operation acts on
+
+    Resources:
+        The resources are derived from the following identity:
+
+        .. math:: \hat{Y} = \hat{S} \cdot \hat{X} \cdot \hat{S}^{\dagger}.
+
+        By replacing the :class:`~.ResourceX` gate with a :class:`~.ResourceCNOT` we
+        obtain the controlled decomposition. Specifically, the resources are defined as a
+        :class:`~.ResourceCNOT` gate conjugated by a pair of :class:`~.ResourceS` gates.
+
+    .. seealso:: :class:`~.CY`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceCY.resource_decomp()
+    [(1 x CNOT), (1 x S), (1 x Adjoint(S))]
+    """
+
+    num_wires = 2
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            dict: Empty dictionary. The resources of this operation don't depend on any additional parameters.
+        """
+        return {}
+
+    @classmethod
+    def resource_rep(cls) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+        return CompressedResourceOp(cls, {})
+
+    @classmethod
+    def default_resource_decomp(cls, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            The resources are derived from the following identity:
+
+            .. math:: \hat{Y} = \hat{S} \cdot \hat{X} \cdot \hat{S}^{\dagger}.
+
+            By replacing the :class:`~.ResourceX` gate with a :class:`~.ResourceCNOT` we
+            obtain the controlled decomposition. Specifically, the resources are defined as a
+            :class:`~.ResourceCNOT` gate conjugated by a pair of :class:`~.ResourceS` gates.
+        """
+        # TODO: To be added after the symbolic PR
+        raise re.ResourcesNotDefined
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            This operation is self-adjoint, so the resources of the adjoint operation results
+            in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep())]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+
+        Resources:
+            This operation is self-inverse, thus when raised to even integer powers acts like
+            the identity operator and raised to odd powers it produces itself.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return (
+            [GateCount(resource_rep(re.ResourceIdentity))]
+            if pow_z % 2 == 0
+            else [GateCount(cls.resource_rep())]
+        )
+
+
+class ResourceCZ(ResourceOperator):
+    r"""Resource class for the CZ gate.
+
+    Args:
+        wires (Sequence[int], optional): the wires the operation acts on
+
+    Resources:
+        The resources are derived from the following identity:
+
+        .. math:: \hat{Z} = \hat{H} \cdot \hat{X} \cdot \hat{H}.
+
+        By replacing the :class:`~.ResourceX` gate with a :class:`~.ResourceCNOT` we obtain
+        the controlled decomposition. Specifically, the resources are defined as a
+        :class:`~.ResourceCNOT` gate conjugated by a pair of :class:`~.ResourceHadamard` gates.
+
+    .. seealso:: :class:`~.CZ`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceCZ.resource_decomp()
+    [(1 x CNOT), (2 x Hadamard)]
+    """
+
+    num_wires = 2
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            dict: Empty dictionary. The resources of this operation don't depend on any additional parameters.
+        """
+        return {}
+
+    @classmethod
+    def resource_rep(cls) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+        return CompressedResourceOp(cls, {})
+
+    @classmethod
+    def default_resource_decomp(cls, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            The resources are derived from the following identity:
+
+            .. math:: \hat{Z} = \hat{H} \cdot \hat{X} \cdot \hat{H}.
+
+            By replacing the :class:`~.ResourceX` gate with a :class:`~.ResourceCNOT` we obtain
+            the controlled decomposition. Specifically, the resources are defined as a
+            :class:`~.ResourceCNOT` gate conjugated by a pair of :class:`~.ResourceHadamard` gates.
+        """
+        cnot = resource_rep(ResourceCNOT)
+        h = resource_rep(re.ResourceHadamard)
+
+        return [GateCount(cnot), GateCount(h, 2)]
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            This operation is self-adjoint, so the resources of the adjoint operation results
+            in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep())]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+
+        Resources:
+            This operation is self-inverse, thus when raised to even integer powers acts like
+            the identity operator and raised to odd powers it produces itself.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return (
+            [GateCount(resource_rep(re.ResourceIdentity))]
+            if pow_z % 2 == 0
+            else [GateCount(cls.resource_rep())]
+        )
+
+
+class ResourceCSWAP(ResourceOperator):
+    r"""Resource class for the CSWAP gate.
+
+    Args:
+        wires (Sequence[int], optional): the wires the operation acts on
+
+    Resources:
+        The resources are taken from Figure 1d of `arXiv:2305.18128 <https://arxiv.org/pdf/2305.18128>`_.
+
+        The circuit which applies the SWAP operation on wires (1, 2) and controlled on wire (0) is
+        defined as:
+
+        .. code-block:: bash
+
+            0: ────╭●────┤
+            1: ─╭X─├●─╭X─┤
+            2: ─╰●─╰X─╰●─┤
+
+    .. seealso:: :class:`~.CSWAP`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceCSWAP.resource_decomp()
+    [(1 x Toffoli), (2 x CNOT)]
+    """
+
+    num_wires = 3
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            dict: Empty dictionary. The resources of this operation don't depend on any additional parameters.
+        """
+        return {}
+
+    @classmethod
+    def resource_rep(cls) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+        return CompressedResourceOp(cls, {})
+
+    @classmethod
+    def default_resource_decomp(cls, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            The resources are taken from Figure 1d of `arXiv:2305.18128 <https://arxiv.org/pdf/2305.18128>`_.
+
+            The circuit which applies the SWAP operation on wires (1, 2) and controlled on wire (0) is
+            defined as:
+
+            .. code-block:: bash
+
+                0: ────╭●────┤
+                1: ─╭X─├●─╭X─┤
+                2: ─╰●─╰X─╰●─┤
+        """
+        tof = resource_rep(ResourceToffoli)
+        cnot = resource_rep(ResourceCNOT)
+        return [GateCount(tof), GateCount(cnot, 2)]
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            This operation is self-adjoint, so the resources of the adjoint operation results
+            in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep())]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+
+        Resources:
+            This operation is self-inverse, thus when raised to even integer powers acts like
+            the identity operator and raised to odd powers it produces itself.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return (
+            [GateCount(resource_rep(re.ResourceIdentity))]
+            if pow_z % 2 == 0
+            else [GateCount(cls.resource_rep())]
+        )
+
+
+class ResourceCCZ(ResourceOperator):
+    r"""Resource class for the CCZ gate.
+
+    Args:
+        wires (Sequence[int], optional): the subsystem the gate acts on
+
+    Resources:
+        The resources are derived from the following identity:
+
+        .. math:: \hat{Z} = \hat{H} \cdot \hat{X} \cdot \hat{H}.
+
+        By replacing the :class:`~.ResourceX` gate with a :class:`~.ResourceToffoli` we obtain
+        the controlled decomposition. Specifically, the resources are defined as a
+        :class:`~.ResourceToffoli` gate conjugated by a pair of :class:`~.ResourceHadamard` gates.
+
+    .. seealso:: :class:`~.CCZ`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceCCZ.resource_decomp()
+    [(1 x Toffoli), (2 x Hadamard)]
+    """
+
+    num_wires = 3
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            dict: Empty dictionary. The resources of this operation don't depend on any additional parameters.
+        """
+        return {}
+
+    @classmethod
+    def resource_rep(cls) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+        return CompressedResourceOp(cls, {})
+
+    @classmethod
+    def default_resource_decomp(cls, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            The resources are derived from the following identity:
+
+            .. math:: \hat{Z} = \hat{H} \cdot \hat{X} \cdot \hat{H}.
+
+            By replacing the :class:`~.ResourceX` gate with a :class:`~.ResourceToffoli` we obtain
+            the controlled decomposition. Specifically, the resources are defined as a
+            :class:`~.ResourceToffoli` gate conjugated by a pair of :class:`~.ResourceHadamard` gates.
+        """
+        toffoli = resource_rep(ResourceToffoli)
+        h = resource_rep(re.ResourceHadamard)
+        return [GateCount(toffoli), GateCount(h, 2)]
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls, **kwargs):
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            This operation is self-adjoint, so the resources of the adjoint operation results
+            in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep())]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+
+        Resources:
+            This operation is self-inverse, thus when raised to even integer powers acts like
+            the identity operator and raised to odd powers it produces itself.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return (
+            [GateCount(resource_rep(re.ResourceIdentity))]
+            if pow_z % 2 == 0
+            else [GateCount(cls.resource_rep())]
+        )
+
+
+class ResourceCNOT(ResourceOperator):
+    r"""Resource class for the CNOT gate.
+
+    Args:
+        wires (Sequence[int], optional): the wires the operation acts on
+
+    Resources:
+        The CNOT gate is treated as a fundamental gate and thus it cannot be decomposed
+        further. Requesting the resources of this gate raises a :code:`ResourcesNotDefined` error.
+
+    .. seealso:: :class:`~.CNOT`
+
+    """
+
+    num_wires = 2
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            dict: Empty dictionary. The resources of this operation don't depend on any additional parameters.
+        """
+        return {}
+
+    @classmethod
+    def resource_rep(cls) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+        return CompressedResourceOp(cls, {})
+
+    @classmethod
+    def default_resource_decomp(cls, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            The CNOT gate is treated as a fundamental gate and thus it cannot be decomposed
+            further. Requesting the resources of this gate raises a :code:`ResourcesNotDefined` error.
+
+        Raises:
+            ResourcesNotDefined: This gate is fundamental, no further decomposition defined.
+        """
+        raise re.ResourcesNotDefined
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            This operation is self-adjoint, so the resources of the adjoint operation results
+            in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep())]
+
+    @classmethod
+    def default_controlled_resource_decomp(
+        cls,
+        ctrl_num_ctrl_wires,
+        ctrl_num_ctrl_values,
+    ) -> list[GateCount]:
+        r"""Returns a list representing the resources for a controlled version of the operator.
+
+        Args:
+            ctrl_num_ctrl_wires (int): the number of qubits the operation is controlled on
+            ctrl_num_ctrl_values (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
+
+        Resources:
+            The resources are expressed as one general :class:`~.ResourceMultiControlledX` gate.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        if ctrl_num_ctrl_wires == 1 and ctrl_num_ctrl_values == 0:
+            return [GateCount(resource_rep(ResourceToffoli))]
+
+        mcx = resource_rep(
+            ResourceMultiControlledX,
+            {
+                "num_ctrl_wires": ctrl_num_ctrl_wires + 1,
+                "num_ctrl_values": ctrl_num_ctrl_values,
+            },
+        )
+        return [
+            GateCount(mcx),
+        ]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+
+        Resources:
+            This operation is self-inverse, thus when raised to even integer powers acts like
+            the identity operator and raised to odd powers it produces itself.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return (
+            [GateCount(resource_rep(re.ResourceIdentity))]
+            if pow_z % 2 == 0
+            else [GateCount(cls.resource_rep())]
+        )
+
+
+class ResourceTempAND(ResourceOperator):
+    r"""Resource class representing a temporary `AND`-gate.
+
+    Args:
+        wires (Sequence[int], optional): the wires the operation acts on
+
+    This gate was introduced in Fig 4 of `Babbush 2018 <https://arxiv.org/pdf/1805.03662>`_ along
+    with it's adjoint (uncompute).
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceTempAND.resource_decomp()
+    [(1 x Toffoli)]
+    """
+
+    num_wires = 3
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            dict: Empty dictionary. The resources of this operation don't depend on any additional parameters.
+        """
+        return {}
+
+    @classmethod
+    def resource_rep(cls) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+        return CompressedResourceOp(cls, {})
+
+    @classmethod
+    def default_resource_decomp(cls, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            The resources are obtained from Figure 4 of `Babbush 2018 <https://arxiv.org/pdf/1805.03662>`_.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        tof = resource_rep(ResourceToffoli, {"elbow": "left"})
+        return [GateCount(tof)]
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            The resources are obtained from Figure 4 of `Babbush 2018 <https://arxiv.org/pdf/1805.03662>`_.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        h = resource_rep(re.ResourceHadamard)
+        cz = resource_rep(ResourceCZ)
+        return [GateCount(h), GateCount(cz)]
+
+    @classmethod
+    def default_controlled_resource_decomp(
+        cls,
+        ctrl_num_ctrl_wires,
+        ctrl_num_ctrl_values,
+    ) -> list[GateCount]:
+        r"""Returns a list representing the resources for a controlled version of the operator.
+
+        Args:
+            ctrl_num_ctrl_wires (int): the number of qubits the operation is controlled on
+            ctrl_num_ctrl_values (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
+
+        Resources:
+            The resources are expressed as one general :class:`~.ResourceMultiControlledX` gate.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        mcx = resource_rep(
+            re.ResourceMultiControlledX,
+            {
+                "num_ctrl_wires": ctrl_num_ctrl_wires + 2,
+                "num_ctrl_values": ctrl_num_ctrl_values,
+            },
+        )
+        return [GateCount(mcx)]
+
+
+class ResourceToffoli(ResourceOperator):
+    r"""Resource class for the Toffoli gate.
+
+    Args:
+        wires (Sequence[int], optional): the subsystem the gate acts on
+        elbow (Union[str, None]): String identifier to determine if this is a special type of
+            Toffoli gate (left or right elbow). Default value is `None`.
+
+    Resources:
+        If `elbow` is provided, resources are obtained from Figure 4 of
+        `Babbush 2018 <https://arxiv.org/pdf/1805.03662>`_.
+
+        If `elbow` is `None`, the resources are obtained from Figure 1 of
+        `Jones 2012 <https://arxiv.org/pdf/1212.5069>`_.
+
+        The circuit which applies the Toffoli gate on target wire 'target' with control wires
+        ('c1', 'c2') is defined as:
+
+        .. code-block:: bash
+
+                c1: ─╭●────╭X──T†────────╭X────╭●───────────────╭●─┤
+                c2: ─│──╭X─│──╭●───T†─╭●─│──╭X─│────────────────╰Z─┤
+              aux1: ─╰X─│──│──╰X───T──╰X─│──│──╰X────────────────║─┤
+              aux2: ──H─╰●─╰●──T─────────╰●─╰●──H──S─╭●──H──┤↗├──║─┤
+            target: ─────────────────────────────────╰X──────║───║─┤
+                                                             ╚═══╝
+
+        Specifically, the resources are defined as nine :class:`~.ResourceCNOT` gates, three
+        :class:`~.ResourceHadamard` gates, one :class:`~.ResourceCZ` gate, one :class:`~.ResourceS`
+        gate, two :class:`~.ResourceT` gates and two adjoint :class:`~.ResourceT` gates.
+
+    .. seealso:: :class:`~.Toffoli`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceToffoli.resource_decomp()
+    [AllocWires(2), (9 x CNOT), (3 x Hadamard), (1 x S), (1 x CZ), (2 x T), (2 x Adjoint(T)), FreeWires(2)]
+    """
+
+    num_wires = 3
+    resource_keys = {"elbow"}
+
+    def __init__(self, elbow=None, wires=None) -> None:
+        self.elbow = elbow
+        super().__init__(wires=wires)
+
+    @classmethod
+    def default_resource_decomp(cls, elbow=None, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            If `elbow` is provided, resources are obtained from Figure 4 of
+            `arXiv:1805.03662 <https://arxiv.org/pdf/1805.03662>`_.
+
+            If `elbow` is `None`, the resources are obtained from Figure 1 of
+            `Jones 2012 <https://arxiv.org/pdf/1212.5069>`_.
+
+            The circuit which applies the Toffoli gate on target wire 'target' with control wires
+            ('c1', 'c2') is defined as:
+
+            .. code-block:: bash
+
+                  c1: ─╭●────╭X──T†────────╭X────╭●───────────────╭●─┤
+                  c2: ─│──╭X─│──╭●───T†─╭●─│──╭X─│────────────────╰Z─┤
+                aux1: ─╰X─│──│──╰X───T──╰X─│──│──╰X────────────────║─┤
+                aux2: ──H─╰●─╰●──T─────────╰●─╰●──H──S─╭●──H──┤↗├──║─┤
+              target: ─────────────────────────────────╰X──────║───║─┤
+                                                               ╚═══╝
+
+            Specifically, the resources are defined as nine :class:`~.ResourceCNOT` gates, three
+            :class:`~.ResourceHadamard` gates, one :class:`~.ResourceCZ` gate, one :class:`~.ResourceS`
+            gate, two :class:`~.ResourceT` gates and two adjoint :class:`~.ResourceT` gates.
+        """
+        # TODO: To be added after the symbolic op PR
+        raise re.ResourcesNotDefined
+
+    @classmethod
+    def textbook_resource_decomp(cls, elbow=None, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            If `elbow` is provided, resources are obtained from Figure 4 of
+            `arXiv:1805.03662 <https://arxiv.org/pdf/1805.03662>`_.
+
+            If `elbow` is `None`, the resources are taken from Figure 4.9 of `Nielsen, M. A., & Chuang, I. L. (2010)
+            <https://www.cambridge.org/highereducation/books/quantum-computation-and-quantum-information/01E10196D0A682A6AEFFEA52D53BE9AE#overview>`_.
+
+            The circuit is defined as:
+
+            .. code-block:: bash
+
+                0: ───────────╭●───────────╭●────╭●──T──╭●─┤
+                1: ────╭●─────│─────╭●─────│───T─╰X──T†─╰X─┤
+                2: ──H─╰X──T†─╰X──T─╰X──T†─╰X──T──H────────┤
+
+            Specifically, the resources are defined as six :class:`~.ResourceCNOT` gates, two
+            :class:`~.ResourceHadamard` gates, four :class:`~.ResourceT` gates and three adjoint
+            :class:`~.ResourceT` gates.
+        """
+        # TODO: To be added after the symbolic op PR
+        raise re.ResourcesNotDefined
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            A dictionary containing the resource parameters:
+                * elbow (Union[str, None]): String identifier to determine if this is a special type of Toffoli gate (left or right elbow).
+
+        """
+        return {"elbow": self.elbow}
+
+    @classmethod
+    def resource_rep(cls, elbow=None) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+        return CompressedResourceOp(cls, {"elbow": elbow})
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls, elbow=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            This operation is self-adjoint, so the resources of the adjoint operation results
+            in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        if elbow is None:
+            return [GateCount(cls.resource_rep())]
+
+        adj_elbow = "left" if elbow == "right" else "right"
+        return [GateCount(cls.resource_rep(elbow=adj_elbow))]
+
+    @classmethod
+    def default_controlled_resource_decomp(
+        cls,
+        ctrl_num_ctrl_wires,
+        ctrl_num_ctrl_values,
+    ) -> list[GateCount]:
+        r"""Returns a list representing the resources for a controlled version of the operator.
+
+        Args:
+            ctrl_num_ctrl_wires (int): the number of qubits the operation is controlled on
+            ctrl_num_ctrl_values (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
+            elbow (Union[str, None]): String identifier to determine if this is a special type of Toffoli gate (left or right elbow).
+                Default value is `None`.
+        Resources:
+            The resources are expressed as one general :class:`~.ResourceMultiControlledX` gate.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        mcx = resource_rep(
+            re.ResourceMultiControlledX,
+            {
+                "num_ctrl_wires": ctrl_num_ctrl_wires + 2,
+                "num_ctrl_values": ctrl_num_ctrl_values,
+            },
+        )
+        return [GateCount(mcx)]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z, elbow=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+
+        Resources:
+            This operation is self-inverse, thus when raised to even integer powers acts like
+            the identity operator and raised to odd powers it produces itself.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return (
+            [GateCount(resource_rep(re.ResourceIdentity))]
+            if pow_z % 2 == 0
+            else [GateCount(cls.resource_rep(elbow=elbow))]
+        )
+
+
+class ResourceMultiControlledX(ResourceOperator):
+    r"""Resource class for the MultiControlledX gate.
+
+    Args:
+        num_ctrl_wires (int): the number of qubits the operation is controlled on
+        num_ctrl_values (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
+        wires (Sequence[int], optional): the wires this operation acts on
+
+    Resources:
+        The resources are obtained based on the unary iteration technique described in
+        `Babbush 2018 <https://arxiv.org/pdf/1805.03662>`_.
+
+    .. seealso:: :class:`~.MultiControlledX`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceMultiControlledX.resource_decomp(num_ctrl_wires=5, num_ctrl_values=2)
+    [(4 x X), AllocWires(3), (3 x TempAND), (3 x Toffoli), (1 x Toffoli), FreeWires(3)]
+    """
+
+    resource_keys = {"num_ctrl_wires", "num_ctrl_values"}
+
+    def __init__(self, num_ctrl_wires, num_ctrl_values, wires=None) -> None:
+        self.num_ctrl_wires = num_ctrl_wires
+        self.num_ctrl_values = num_ctrl_values
+
+        self.num_wires = num_ctrl_wires + 1
+        super().__init__(wires=wires)
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            dict: A dictionary containing the resource parameters:
+                * num_ctrl_wires (int): the number of qubits the operation is controlled on
+                * num_ctrl_values (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
+        """
+
+        return {
+            "num_ctrl_wires": self.num_ctrl_wires,
+            "num_ctrl_values": self.num_ctrl_values,
+        }
+
+    @classmethod
+    def resource_rep(cls, num_ctrl_wires, num_ctrl_values) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources.
+
+        Args:
+            num_ctrl_wires (int): the number of qubits the operation is controlled on
+            num_ctrl_values (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
+
+        Returns:
+            CompressedResourceOp: the operator in a compressed representation
+        """
+        return CompressedResourceOp(
+            cls,
+            {
+                "num_ctrl_wires": num_ctrl_wires,
+                "num_ctrl_values": num_ctrl_values,
+            },
+        )
+
+    @classmethod
+    def default_resource_decomp(
+        cls,
+        num_ctrl_wires,
+        num_ctrl_values,
+        **kwargs,  # pylint: disable=unused-argument
+    ) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Args:
+            num_ctrl_wires (int): the number of qubits the operation is controlled on
+            num_ctrl_values (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
+
+        Resources:
+            The resources are obtained based on the unary iteration technique described in
+            `Babbush 2018 <https://arxiv.org/pdf/1805.03662>`_.
+        """
+        gate_lst = []
+
+        x = resource_rep(re.ResourceX)
+        if num_ctrl_wires == 0:
+            if num_ctrl_values:
+                return []
+
+            return [GateCount(x)]
+
+        if num_ctrl_values:
+            gate_lst.append(GateCount(x, num_ctrl_values * 2))
+
+        cnot = resource_rep(ResourceCNOT)
+        if num_ctrl_wires == 1:
+            gate_lst.append(GateCount(cnot))
+            return gate_lst
+
+        toffoli = resource_rep(ResourceToffoli)
+        if num_ctrl_wires == 2:
+            gate_lst.append(GateCount(toffoli))
+            return gate_lst
+
+        if num_ctrl_wires == 3:  # assuming one work wire:
+            res = [AllocWires(1), GateCount(cnot, 2), GateCount(toffoli, 2), FreeWires(1)]
+            gate_lst.extend(res)
+            return gate_lst
+
+        l_elbow = resource_rep(ResourceTempAND)
+        r_elbow = resource_rep(ResourceToffoli, {"elbow": "right"})
+
+        res = [
+            AllocWires(num_ctrl_wires - 2),
+            GateCount(l_elbow, num_ctrl_wires - 2),
+            GateCount(r_elbow, num_ctrl_wires - 2),
+            GateCount(toffoli, 1),
+            FreeWires(num_ctrl_wires - 2),
+        ]
+        gate_lst.extend(res)
+        return gate_lst
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls, num_ctrl_wires, num_ctrl_values) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Args:
+            num_ctrl_wires (int): the number of qubits the operation is controlled on
+            num_ctrl_values (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
+
+        Resources:
+            This operation is self-adjoint, so the resources of the adjoint operation results
+            in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep(num_ctrl_wires, num_ctrl_values))]
+
+    @classmethod
+    def default_controlled_resource_decomp(
+        cls,
+        ctrl_num_ctrl_wires,
+        ctrl_num_ctrl_values,
+        num_ctrl_wires,
+        num_ctrl_values,
+    ) -> list[GateCount]:
+        r"""Returns a list representing the resources for a controlled version of the operator.
+
+        Args:
+            ctrl_num_ctrl_wires (int): The number of control qubits to further control the base
+                controlled operation upon.
+            ctrl_num_ctrl_values (int): The subset of those control qubits, which further control
+                the base controlled operation, which are controlled when in the :math:`|0\rangle` state.
+            num_ctrl_wires (int): the number of control qubits of the operation
+            num_ctrl_values (int): The subset of control qubits of the operation, that are controlled
+                when in the :math:`|0\rangle` state.
+
+        Resources:
+            The resources are derived by combining the control qubits, control-values and
+            into a single instance of :class:`~.ResourceMultiControlledX` gate, controlled
+            on the whole set of control-qubits.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [
+            GateCount(
+                cls.resource_rep(
+                    ctrl_num_ctrl_wires + num_ctrl_wires,
+                    ctrl_num_ctrl_values + num_ctrl_values,
+                )
+            )
+        ]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z, num_ctrl_wires, num_ctrl_values) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+            num_ctrl_wires (int): the number of qubits the operation is controlled on
+            num_ctrl_values (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
+
+        Resources:
+            This operation is self-inverse, thus when raised to even integer powers acts like
+            the identity operator and raised to odd powers it produces itself.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return (
+            [GateCount(resource_rep(re.ResourceIdentity))]
+            if pow_z % 2 == 0
+            else [GateCount(cls.resource_rep(num_ctrl_wires, num_ctrl_values))]
+        )
+
+
+class ResourceCRX(ResourceOperator):
+    r"""Resource class for the CRX gate.
+
+    Args:
+        wires (Sequence[int], optional): the wire the operation acts on
+        eps (float, optional): The error threshold for clifford plus T decomposition of the rotation gate.
+            The default value is `None` which corresponds to using the epsilon stated in the config.
+
+    Resources:
+        The resources are taken from Figure 1b of `Gheorghiu, V., Mosca, M. & Mukhopadhyay
+        <https://arxiv.org/pdf/2110.10292>`_. In combination with the following identity:
+
+        .. math:: \hat{RX} = \hat{H} \cdot \hat{RZ}  \cdot \hat{H},
+
+        we can express the :code:`CRX` gate as a :code:`CRZ` gate conjugated by :code:`Hadamard`
+        gates. The expression for controlled-RZ gates is used as defined in the reference above.
+        Specifically, the resources are defined as two :class:`~.ResourceCNOT` gates, two
+        :class:`~.ResourceHadamard` gates and two :class:`~.ResourceRZ` gates.
+
+    .. seealso:: :class:`~.CRX`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceCRX.resource_decomp()
+    [(2 x CNOT), (2 x RZ), (2 x Hadamard)]
+    """
+
+    num_wires = 2
+
+    def __init__(self, eps=None, wires=None) -> None:
+        self.eps = eps
+        super().__init__(wires=wires)
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            A dictionary containing the resource parameters:
+                * eps (Union[float, None]): error threshold for the approximation
+        """
+        return {"eps": self.eps}
+
+    @classmethod
+    def resource_rep(cls, eps=None) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+
+        return CompressedResourceOp(cls, {"eps": eps})
+
+    @classmethod
+    def default_resource_decomp(cls, eps=None, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            The resources are taken from Figure 1b of `Gheorghiu, V., Mosca, M. & Mukhopadhyay
+            <https://arxiv.org/pdf/2110.10292>`_. In combination with the following identity:
+
+            .. math:: \hat{RX} = \hat{H} \cdot \hat{RZ}  \cdot \hat{H},
+
+            we can express the :code:`CRX` gate as a :code:`CRZ` gate conjugated by :code:`Hadamard`
+            gates. The expression for controlled-RZ gates is used as defined in the reference above.
+            Specifically, the resources are defined as two :class:`~.ResourceCNOT` gates, two
+            :class:`~.ResourceHadamard` gates and two :class:`~.ResourceRZ` gates.
+        """
+        h = resource_rep(re.ResourceHadamard)
+        rz = resource_rep(re.ResourceRZ, {"eps": eps})
+        cnot = resource_rep(ResourceCNOT)
+
+        return [GateCount(cnot, 2), GateCount(rz, 2), GateCount(h, 2)]
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls, eps=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            The adjoint of a single qubit rotation changes the sign of the rotation angle,
+            thus the resources of the adjoint operation result in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep(eps))]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z, eps=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+            eps (float, optional): The error threshold for clifford plus T decomposition of the rotation gate.
+                The default value is `None` which corresponds to using the epsilon stated in the config.
+
+        Resources:
+            Taking arbitrary powers of a single qubit rotation produces a sum of rotations.
+            The resources simplify to just one total single qubit rotation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+
+        return [GateCount(cls.resource_rep(eps))]
+
+
+class ResourceCRY(ResourceOperator):
+    r"""Resource class for the CRY gate.
+
+    Args:
+        wires (Sequence[int], optional): the wire the operation acts on
+        eps (float, optional): The error threshold for clifford plus T decomposition of the rotation gate.
+            The default value is `None` which corresponds to using the epsilon stated in the config.
+
+    Resources:
+        The resources are taken from Figure 1b of `Gheorghiu, V., Mosca, M. & Mukhopadhyay
+        <https://arxiv.org/pdf/2110.10292>`_. In combination with the following identity:
+
+        .. math:: \hat{RY}(\theta) = \hat{X} \cdot \hat{RY}(- \theta) \cdot \hat{X}.
+
+        By replacing the :class:`~.ResourceX` gates with :class:`~.ResourceCNOT` gates, we obtain a
+        controlled-version of this identity. Thus we are able to constructively or destructively
+        interfere the gates based on the value of the control qubit. Specifically, the resources are
+        defined as two :class:`~.ResourceCNOT` gates and two :class:`~.ResourceRY` gates.
+
+    .. seealso:: :class:`~.CRY`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceCRY.resource_decomp()
+    [(2 x CNOT), (2 x RY)]
+    """
+
+    num_wires = 2
+
+    def __init__(self, eps=None, wires=None) -> None:
+        self.eps = eps
+        super().__init__(wires=wires)
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            A dictionary containing the resource parameters:
+                * eps (Union[float, None]): error threshold for the approximation
+
+        """
+        return {"eps": self.eps}
+
+    @classmethod
+    def resource_rep(cls, eps=None) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+
+        return CompressedResourceOp(cls, {"eps": eps})
+
+    @classmethod
+    def default_resource_decomp(cls, eps=None, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            The resources are taken from Figure 1b of `Gheorghiu, V., Mosca, M. & Mukhopadhyay
+            <https://arxiv.org/pdf/2110.10292>`_. In combination with the following identity:
+
+            .. math:: \hat{RY}(\theta) = \hat{X} \cdot \hat{RY}(- \theta) \cdot \hat{X}.
+
+            By replacing the :code:`X` gates with :code:`CNOT` gates, we obtain a controlled-version of this
+            identity. Thus we are able to constructively or destructively interfere the gates based on the value
+            of the control qubit. Specifically, the resources are defined as two :class:`~.ResourceCNOT` gates
+            and two :class:`~.ResourceRY` gates.
+        """
+        cnot = resource_rep(ResourceCNOT)
+        ry = resource_rep(re.ResourceRY, {"eps": eps})
+        return [GateCount(cnot, 2), GateCount(ry, 2)]
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls, eps=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            The adjoint of a single qubit rotation changes the sign of the rotation angle,
+            thus the resources of the adjoint operation result in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep(eps))]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z, eps=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+            eps (float, optional): The error threshold for clifford plus T decomposition of the rotation gate.
+                The default value is `None` which corresponds to using the epsilon stated in the config.
+
+        Resources:
+            Taking arbitrary powers of a single qubit rotation produces a sum of rotations.
+            The resources simplify to just one total single qubit rotation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+
+        return [GateCount(cls.resource_rep(eps))]
+
+
+class ResourceCRZ(ResourceOperator):
+    r"""Resource class for the CRZ gate.
+
+    Args:
+        wires (Sequence[int], optional): the wire the operation acts on
+        eps (float, optional): The error threshold for clifford plus T decomposition of the rotation gate.
+            The default value is `None` which corresponds to using the epsilon stated in the config.
+
+    Resources:
+        The resources are taken from Figure 1b of `Gheorghiu, V., Mosca, M. & Mukhopadhyay
+        <https://arxiv.org/pdf/2110.10292>`_. In combination with the following identity:
+
+        .. math:: \hat{RZ}(\theta) = \hat{X} \cdot \hat{RZ}(- \theta) \cdot \hat{X}.
+
+        By replacing the :code:`X` gates with :code:`CNOT` gates, we obtain a controlled-version of this
+        identity. Thus we are able to constructively or destructively interfere the gates based on the value
+        of the control qubit. Specifically, the resources are defined as two :class:`~.ResourceCNOT` gates
+        and two :class:`~.ResourceRZ` gates.
+
+    .. seealso:: :class:`~.CRZ`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceCRZ.resource_decomp()
+    [(2 x CNOT), (2 x RZ)]
+    """
+
+    num_wires = 2
+
+    def __init__(self, eps=None, wires=None) -> None:
+        self.eps = eps
+        super().__init__(wires=wires)
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            A dictionary containing the resource parameters:
+                * eps (Union[float, None]): error threshold for the approximation
+        """
+        return {"eps": self.eps}
+
+    @classmethod
+    def resource_rep(cls, eps=None) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+
+        return CompressedResourceOp(cls, {"eps": eps})
+
+    @classmethod
+    def default_resource_decomp(cls, eps=None, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            The resources are taken from Figure 1b of `Gheorghiu, V., Mosca, M. & Mukhopadhyay
+            <https://arxiv.org/pdf/2110.10292>`_. In combination with the following identity:
+
+            .. math:: \hat{RZ}(\theta) = \hat{X} \cdot \hat{RZ}(- \theta) \cdot \hat{X}.
+
+            By replacing the :code:`X` gates with :code:`CNOT` gates, we obtain a controlled-version of this
+            identity. Thus we are able to constructively or destructively interfere the gates based on the value
+            of the control qubit. Specifically, the resources are defined as two :class:`~.ResourceCNOT` gates
+            and two :class:`~.ResourceRZ` gates.
+        """
+        cnot = resource_rep(ResourceCNOT)
+        rz = resource_rep(re.ResourceRZ, {"eps": eps})
+        return [GateCount(cnot, 2), GateCount(rz, 2)]
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls, eps=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            The adjoint of a single qubit rotation changes the sign of the rotation angle,
+            thus the resources of the adjoint operation result in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep(eps))]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z, eps=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+            eps (float, optional): The error threshold for clifford plus T decomposition of the rotation gate.
+                The default value is `None` which corresponds to using the epsilon stated in the config.
+
+        Resources:
+            Taking arbitrary powers of a single qubit rotation produces a sum of rotations.
+            The resources simplify to just one total single qubit rotation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+
+        return [GateCount(cls.resource_rep(eps))]
+
+
+class ResourceCRot(ResourceOperator):
+    r"""Resource class for the CRot gate.
+
+    Args:
+        wires (Sequence[int], optional): the wire the operation acts on
+        eps (float, optional): The error threshold for clifford plus T decomposition of the rotation gate.
+            The default value is `None` which corresponds to using the epsilon stated in the config.
+
+    Resources:
+        The resources are taken from Figure 1b of `Gheorghiu, V., Mosca, M. & Mukhopadhyay
+        <https://arxiv.org/pdf/2110.10292>`_. In combination with the following identity:
+
+        .. math::
+
+            \begin{align}
+                \hat{RZ}(\theta) = \hat{X} \cdot \hat{RZ}(- \theta) \cdot \hat{X}, \\
+                \hat{RY}(\theta) = \hat{X} \cdot \hat{RY}(- \theta) \cdot \hat{X}.
+            \end{align}
+
+        This identity is applied along with some clever choices for the angle values to combine rotation;
+        the final circuit takes the form:
+
+        .. code-block:: bash
+
+            ctrl: ─────╭●─────────╭●─────────┤
+            trgt: ──RZ─╰X──RZ──RY─╰X──RY──RZ─┤
+
+    .. seealso:: :class:`~.CRot`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceCRot.resource_decomp()
+    [(2 x CNOT), (3 x RZ), (2 x RY)]
+    """
+
+    num_wires = 2
+
+    def __init__(self, eps=None, wires=None) -> None:
+        self.eps = eps
+        super().__init__(wires=wires)
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            A dictionary containing the resource parameters:
+                * eps (Union[float, None]): error threshold for the approximation
+
+        """
+        return {"eps": self.eps}
+
+    @classmethod
+    def resource_rep(cls, eps=None) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+
+        return CompressedResourceOp(cls, {"eps": eps})
+
+    @classmethod
+    def default_resource_decomp(cls, eps=None, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+
+        Resources:
+            The resources are taken from Figure 1b of `Gheorghiu, V., Mosca, M. & Mukhopadhyay
+            <https://arxiv.org/pdf/2110.10292>`_. In combination with the following identity:
+
+            .. math::
+
+                \begin{align}
+                    \hat{RZ}(\theta) = \hat{X} \cdot \hat{RZ}(- \theta) \cdot \hat{X}, \\
+                    \hat{RY}(\theta) = \hat{X} \cdot \hat{RY}(- \theta) \cdot \hat{X}.
+                \end{align}
+
+            This identity is applied along with some clever choices for the angle values to combine rotation;
+            the final circuit takes the form:
+
+            .. code-block:: bash
+
+                ctrl: ─────╭●─────────╭●─────────┤
+                trgt: ──RZ─╰X──RZ──RY─╰X──RY──RZ─┤
+
+        """
+        cnot = resource_rep(ResourceCNOT)
+        rz = resource_rep(re.ResourceRZ, {"eps": eps})
+        ry = resource_rep(re.ResourceRY, {"eps": eps})
+
+        return [GateCount(cnot, 2), GateCount(rz, 3), GateCount(ry, 2)]
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls, eps=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            The adjoint of a general rotation flips the sign of the rotation angle,
+            thus the resources of the adjoint operation result in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep(eps))]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z, eps=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+            eps (float, optional): The error threshold for clifford plus T decomposition of the rotation gate.
+                The default value is `None` which corresponds to using the epsilon stated in the config.
+
+        Resources:
+            Taking arbitrary powers of a general single qubit rotation produces a sum of rotations.
+            The resources simplify to just one total single qubit rotation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+
+        return [GateCount(cls.resource_rep(eps))]
+
+
+class ResourceControlledPhaseShift(ResourceOperator):
+    r"""Resource class for the ControlledPhaseShift gate.
+
+    Args:
+        wires (Sequence[int], optional): the wire the operation acts on
+        eps (float, optional): The error threshold for clifford plus T decomposition of the rotation gate.
+            The default value is `None` which corresponds to using the epsilon stated in the config.
+
+    Resources:
+        The resources are derived using the fact that a :class:`~.ResourcePhaseShift` gate is
+        identical to the :class:`~.ResourceRZ` gate up to some global phase. Furthermore, a controlled
+        global phase simplifies to a :class:`~.ResourcePhaseShift` gate. This gives rise to the
+        following identity:
+
+        .. math:: CR_\phi(\phi) = (R_\phi(\phi/2) \otimes I) \cdot CNOT \cdot (I \otimes R_\phi(-\phi/2)) \cdot CNOT \cdot (I \otimes R_\phi(\phi/2))
+
+        Specifically, the resources are defined as two :class:`~.ResourceCNOT` gates and three
+        :class:`~.ResourceRZ` gates.
+
+    .. seealso:: :class:`~.ControlledPhaseShift`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> re.ResourceControlledPhaseShift.resource_decomp()
+    [(2 x CNOT), (3 x RZ)]
+    """
+
+    num_wires = 2
+
+    def __init__(self, eps=None, wires=None) -> None:
+        self.eps = eps
+        super().__init__(wires=wires)
+
+    @property
+    def resource_params(self):
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            A dictionary containing the resource parameters:
+                * eps (Union[float, None]): error threshold for the approximation
+
+        """
+        return {"eps": self.eps}
+
+    @classmethod
+    def resource_rep(cls, eps=None) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources."""
+
+        return CompressedResourceOp(cls, {"eps": eps})
+
+    @classmethod
+    def default_resource_decomp(cls, eps=None, **kwargs) -> list[GateCount]:
+        r"""Returns a list of GateCount objects representing the resources of the operator.
+        Each GateCount object specifies a gate type and its total occurrence count.
+        """
+        cnot = resource_rep(ResourceCNOT)
+        rz = resource_rep(re.ResourceRZ, {"eps": eps})
+        return [GateCount(cnot, 2), GateCount(rz, 3)]
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls, eps=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for the adjoint of the operator.
+
+        Resources:
+            The adjoint of a phase shift just flips the sign of the phase angle,
+            thus the resources of the adjoint operation result in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep(eps))]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z, eps=None) -> list[GateCount]:
+        r"""Returns a list representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+            eps (float, optional): The error threshold for clifford plus T decomposition of the rotation gate.
+                The default value is `None` which corresponds to using the epsilon stated in the config.
+
+        Resources:
+            Taking arbitrary powers of a phase shift produces a sum of shifts.
+            The resources simplify to just one total phase shift operator.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+
+        return [GateCount(cls.resource_rep(eps))]
diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py
index 4d7f72fb9..2fb306edc 100644
--- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py
+++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py
@@ -20,6 +20,7 @@ from .non_parametric_ops import (
     ResourceX,
     ResourceY,
     ResourceZ,
+    ResourceSWAP,
 )
 from .parametric_ops_single_qubit import (
     ResourcePhaseShift,
diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py
index 3a56f030a..8a1c7d714 100644
--- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py
+++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py
@@ -199,6 +199,189 @@ class ResourceS(ResourceOperator):
         return [GateCount(resource_rep(ResourceZ)), GateCount(cls.resource_rep())]
 
 
+class ResourceSWAP(ResourceOperator):
+    r"""Resource class for the SWAP gate.
+
+    Args:
+        wires (Sequence[int], optional): the wires the operation acts on
+
+    Resources:
+        The resources come from the following identity expressing SWAP as the product of
+        three :class:`~.CNOT` gates:
+
+        .. math::
+
+            SWAP = \begin{bmatrix}
+                        1 & 0 & 0 & 0 \\
+                        0 & 0 & 1 & 0\\
+                        0 & 1 & 0 & 0\\
+                        0 & 0 & 0 & 1
+                    \end{bmatrix}
+            =  \begin{bmatrix}
+                    1 & 0 & 0 & 0 \\
+                    0 & 1 & 0 & 0\\
+                    0 & 0 & 0 & 1\\
+                    0 & 0 & 1 & 0
+                \end{bmatrix}
+                \begin{bmatrix}
+                    1 & 0 & 0 & 0 \\
+                    0 & 0 & 0 & 1\\
+                    0 & 0 & 1 & 0\\
+                    0 & 1 & 0 & 0
+                \end{bmatrix}
+                \begin{bmatrix}
+                    1 & 0 & 0 & 0 \\
+                    0 & 1 & 0 & 0\\
+                    0 & 0 & 0 & 1\\
+                    0 & 0 & 1 & 0
+            \end{bmatrix}.
+
+    .. seealso:: :class:`~.SWAP`
+
+    **Example**
+
+    The resources for this operation are computed using:
+
+    >>> plre.ResourceSWAP.resource_decomp()
+    [(3 x CNOT)]
+
+    """
+
+    num_wires = 2
+
+    @property
+    def resource_params(self) -> dict:
+        r"""Returns a dictionary containing the minimal information needed to compute the resources.
+
+        Returns:
+            dict: Empty dictionary. The resources of this operation don't depend on any additional parameters.
+        """
+        return {}
+
+    @classmethod
+    def resource_rep(cls) -> CompressedResourceOp:
+        r"""Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute a resource estimation."""
+        return CompressedResourceOp(cls, {})
+
+    @classmethod
+    def default_resource_decomp(cls, **kwargs) -> list[GateCount]:
+        r"""Returns a list representing the resources of the operator. Each object represents a quantum gate
+        and the number of times it occurs in the decomposition.
+
+        Resources:
+            The resources come from the following identity expressing SWAP as the product of
+            three CNOT gates:
+
+            .. math::
+
+                SWAP = \begin{bmatrix}
+                            1 & 0 & 0 & 0 \\
+                            0 & 0 & 1 & 0\\
+                            0 & 1 & 0 & 0\\
+                            0 & 0 & 0 & 1
+                        \end{bmatrix}
+                =  \begin{bmatrix}
+                        1 & 0 & 0 & 0 \\
+                        0 & 1 & 0 & 0\\
+                        0 & 0 & 0 & 1\\
+                        0 & 0 & 1 & 0
+                    \end{bmatrix}
+                    \begin{bmatrix}
+                        1 & 0 & 0 & 0 \\
+                        0 & 0 & 0 & 1\\
+                        0 & 0 & 1 & 0\\
+                        0 & 1 & 0 & 0
+                    \end{bmatrix}
+                    \begin{bmatrix}
+                        1 & 0 & 0 & 0 \\
+                        0 & 1 & 0 & 0\\
+                        0 & 0 & 0 & 1\\
+                        0 & 0 & 1 & 0
+                \end{bmatrix}.
+        """
+        cnot = resource_rep(plre.ResourceCNOT)
+        return [GateCount(cnot, 3)]
+
+    @classmethod
+    def default_adjoint_resource_decomp(cls) -> list[GateCount]:
+        r"""Returns a dictionary representing the resources for the adjoint of the operator.
+
+        Resources:
+            This operation is self-adjoint, so the resources of the adjoint operation results
+            in the original operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        return [GateCount(cls.resource_rep())]
+
+    @classmethod
+    def default_controlled_resource_decomp(
+        cls, ctrl_num_ctrl_wires, ctrl_num_ctrl_values
+    ) -> list[GateCount]:
+        r"""Returns a dictionary representing the resources for a controlled version of the operator.
+
+        Args:
+            ctrl_num_ctrl_wires (int): the number of qubits the operation is controlled on
+            ctrl_num_ctrl_values (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
+
+        Resources:
+            For a single control wire, the cost is a single instance of :class:`~.ResourceCSWAP`.
+            Two additional :class:`~.ResourceX` gates are used to flip the control qubit if
+            it is zero-controlled.
+
+            In the case where multiple controlled wires are provided, the resources are given by
+            two :class:`~.ResourceCNOT` gates and one :class:`~.ResourceMultiControlledX` gate. This
+            is because of the symmetric resource decomposition of the SWAP gate. By controlling on
+            the middle CNOT gate, we obtain the required controlled operation.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        if ctrl_num_ctrl_wires == 1:
+            gate_types = [GateCount(resource_rep(plre.ResourceCSWAP))]
+
+            if ctrl_num_ctrl_values:
+                gate_types.append(GateCount(resource_rep(ResourceX), 2))
+
+            return gate_types
+
+        cnot = resource_rep(plre.ResourceCNOT)
+        mcx = resource_rep(
+            plre.ResourceMultiControlledX,
+            {
+                "num_ctrl_wires": ctrl_num_ctrl_wires + 1,
+                "num_ctrl_values": ctrl_num_ctrl_values,
+            },
+        )
+        return [GateCount(cnot, 2), GateCount(mcx)]
+
+    @classmethod
+    def default_pow_resource_decomp(cls, pow_z) -> list[GateCount]:
+        r"""Returns a dictionary representing the resources for an operator raised to a power.
+
+        Args:
+            pow_z (int): the power that the operator is being raised to
+
+        Resources:
+            The SWAP gate raised to even powers produces identity and raised
+            to odd powers it produces itself.
+
+        Returns:
+            list[GateCount]: A list of GateCount objects, where each object
+            represents a specific quantum gate and the number of times it appears
+            in the decomposition.
+        """
+        if pow_z % 2 == 0:
+            return [GateCount(resource_rep(plre.ResourceIdentity))]
+        return [GateCount(cls.resource_rep())]
+
+
 class ResourceT(ResourceOperator):
     r"""Resource class for the T-gate.
 


Test patch:
diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py
new file mode 100644
index 000000000..7a4a9871e
--- /dev/null
+++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py
@@ -0,0 +1,725 @@
+# 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.
+"""
+Tests for controlled resource operators.
+"""
+import pytest
+
+import pennylane.labs.resource_estimation as re
+from pennylane.labs.resource_estimation.qubit_manager import AllocWires, FreeWires
+
+# pylint: disable=no-self-use, use-implicit-booleaness-not-comparison,too-many-arguments,too-many-positional-arguments
+
+
+class TestResourceCH:
+    """Test the ResourceCH operation"""
+
+    op = re.ResourceCH(wires=[0, 1])
+
+    def test_resources(self):
+        """Test that the resources method produces the expected resources."""
+
+        expected_resources = [
+            re.GateCount(re.ResourceHadamard.resource_rep(), 2),
+            re.GateCount(re.ResourceRY.resource_rep(), 2),
+            re.GateCount(re.ResourceCNOT.resource_rep(), 1),
+        ]
+        assert self.op.resource_decomp(**self.op.resource_params) == expected_resources
+
+    def test_resource_rep(self):
+        """Test the resource_rep produces the correct compressed representation."""
+        expected_rep = re.CompressedResourceOp(re.ResourceCH, {})
+        assert self.op.resource_rep(**self.op.resource_params) == expected_rep
+
+    def test_resource_params(self):
+        """Test that the resource_params are produced as expected."""
+        expected_params = {}
+        assert self.op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        expected_res = [re.GateCount(self.op.resource_rep(), 1)]
+
+        assert self.op.adjoint_resource_decomp() == expected_res
+
+    pow_data = (
+        (1, [re.GateCount(op.resource_rep(), 1)]),
+        (2, [re.GateCount(re.ResourceIdentity.resource_rep(), 1)]),
+        (5, [re.GateCount(op.resource_rep(), 1)]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+
+        assert self.op.pow_resource_decomp(z) == expected_res
+
+
+class TestResourceCY:
+    """Test the ResourceCY operation"""
+
+    op = re.ResourceCY(wires=[0, 1])
+
+    def test_resource_rep(self):
+        """Test the resource_rep produces the correct compressed representation."""
+        expected_rep = re.CompressedResourceOp(re.ResourceCY, {})
+        assert self.op.resource_rep(**self.op.resource_params) == expected_rep
+
+    def test_resource_params(self):
+        """Test that the resource_params are produced as expected."""
+        expected_params = {}
+        assert self.op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        expected_res = [re.GateCount(self.op.resource_rep(), 1)]
+
+        assert self.op.adjoint_resource_decomp() == expected_res
+
+    pow_data = (
+        (1, [re.GateCount(op.resource_rep(), 1)]),
+        (2, [re.GateCount(re.ResourceIdentity.resource_rep(), 1)]),
+        (5, [re.GateCount(op.resource_rep(), 1)]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+
+        assert self.op.pow_resource_decomp(z) == expected_res
+
+
+class TestResourceCZ:
+    """Test the ResourceCZ operation"""
+
+    op = re.ResourceCZ(wires=[0, 1])
+
+    def test_resources(self):
+        """Test that the resources method produces the expected resources."""
+
+        expected_resources = [
+            re.GateCount(re.ResourceCNOT.resource_rep(), 1),
+            re.GateCount(re.ResourceHadamard.resource_rep(), 2),
+        ]
+        assert self.op.resource_decomp(**self.op.resource_params) == expected_resources
+
+    def test_resource_rep(self):
+        """Test the resource_rep produces the correct compressed representation."""
+        expected_rep = re.CompressedResourceOp(re.ResourceCZ, {})
+        assert self.op.resource_rep() == expected_rep
+
+    def test_resource_params(self):
+        """Test that the resource_params are produced as expected."""
+        expected_params = {}
+        assert self.op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        expected_res = [re.GateCount(self.op.resource_rep(), 1)]
+
+        assert self.op.adjoint_resource_decomp() == expected_res
+
+    pow_data = [
+        (1, [re.GateCount(op.resource_rep(), 1)]),
+        (2, [re.GateCount(re.ResourceIdentity.resource_rep(), 1)]),
+        (5, [re.GateCount(op.resource_rep(), 1)]),
+    ]
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+
+        assert self.op.pow_resource_decomp(z) == expected_res
+
+
+class TestResourceCSWAP:
+    """Test the ResourceCSWAP operation"""
+
+    op = re.ResourceCSWAP(wires=[0, 1, 2])
+
+    def test_resources(self):
+        """Test that the resources method produces the expected resources."""
+        expected_resources = [
+            re.GateCount(re.ResourceToffoli.resource_rep(), 1),
+            re.GateCount(re.ResourceCNOT.resource_rep(), 2),
+        ]
+        assert self.op.resource_decomp(**self.op.resource_params) == expected_resources
+
+    def test_resource_rep(self):
+        """Test the resource_rep produces the correct compressed representation."""
+        expected_rep = re.CompressedResourceOp(re.ResourceCSWAP, {})
+        assert self.op.resource_rep(**self.op.resource_params) == expected_rep
+
+    def test_resource_params(self):
+        """Test that the resource_params are produced as expected."""
+        expected_params = {}
+        assert self.op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        expected_res = [re.GateCount(self.op.resource_rep(), 1)]
+        assert self.op.adjoint_resource_decomp() == expected_res
+
+    pow_data = (
+        (1, [re.GateCount(op.resource_rep(), 1)]),
+        (2, [re.GateCount(re.ResourceIdentity.resource_rep(), 1)]),
+        (5, [re.GateCount(op.resource_rep(), 1)]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+
+        assert self.op.pow_resource_decomp(z) == expected_res
+
+
+class TestResourceCCZ:
+    """Test the ResourceCZZ operation"""
+
+    op = re.ResourceCCZ(wires=[0, 1, 2])
+
+    def test_resources(self):
+        """Test that the resources method produces the expected resources."""
+        expected_resources = [
+            re.GateCount(re.ResourceToffoli.resource_rep(), 1),
+            re.GateCount(re.ResourceHadamard.resource_rep(), 2),
+        ]
+        assert self.op.resource_decomp(**self.op.resource_params) == expected_resources
+
+    def test_resource_rep(self):
+        """Test the resource_rep produces the correct compressed representation."""
+        expected_rep = re.CompressedResourceOp(re.ResourceCCZ, {})
+        assert self.op.resource_rep(**self.op.resource_params) == expected_rep
+
+    def test_resource_params(self):
+        """Test that the resource_params are produced as expected."""
+        expected_params = {}
+        assert self.op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        expected_res = [re.GateCount(self.op.resource_rep(), 1)]
+
+        assert self.op.adjoint_resource_decomp() == expected_res
+
+    pow_data = (
+        (1, [re.GateCount(op.resource_rep(), 1)]),
+        (2, [re.GateCount(re.ResourceIdentity.resource_rep(), 1)]),
+        (5, [re.GateCount(op.resource_rep(), 1)]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+
+        assert self.op.pow_resource_decomp(z) == expected_res
+
+
+class TestResourceCNOT:
+    """Test ResourceCNOT operation"""
+
+    op = re.ResourceCNOT([0, 1])
+
+    def test_resources(self):
+        """Test that the resources method is not implemented"""
+        with pytest.raises(re.ResourcesNotDefined):
+            self.op.resource_decomp()
+
+    def test_resource_rep(self):
+        """Test the resource_rep produces the correct compressed representation."""
+        expected = re.CompressedResourceOp(re.ResourceCNOT, {})
+        assert self.op.resource_rep() == expected
+
+    def test_resource_params(self):
+        """Test that the resource_params are produced as expected."""
+        expected_params = {}
+        assert self.op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        expected_res = [re.GateCount(self.op.resource_rep(), 1)]
+
+        assert self.op.adjoint_resource_decomp() == expected_res
+
+    ctrl_data = (
+        (
+            ["c1"],
+            [1],
+            [re.GateCount(re.ResourceToffoli.resource_rep(), 1)],
+        ),
+        (
+            ["c1", "c2"],
+            [1, 1],
+            [re.GateCount(re.ResourceMultiControlledX.resource_rep(3, 0), 1)],
+        ),
+        (
+            ["c1", "c2", "c3"],
+            [1, 0, 0],
+            [re.GateCount(re.ResourceMultiControlledX.resource_rep(4, 2), 1)],
+        ),
+    )
+
+    @pytest.mark.parametrize(
+        "ctrl_wires, ctrl_values, expected_res",
+        ctrl_data,
+    )
+    def test_resource_controlled(self, ctrl_wires, ctrl_values, expected_res):
+        """Test that the controlled resources are as expected"""
+        num_ctrl_wires = len(ctrl_wires)
+        num_ctrl_values = len([v for v in ctrl_values if not v])
+
+        assert self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values) == expected_res
+
+    pow_data = (
+        (1, [re.GateCount(op.resource_rep(), 1)]),
+        (2, [re.GateCount(re.ResourceIdentity.resource_rep(), 1)]),
+        (5, [re.GateCount(op.resource_rep(), 1)]),
+        (8, [re.GateCount(re.ResourceIdentity.resource_rep(), 1)]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+
+        assert self.op.pow_resource_decomp(z) == expected_res
+
+
+class TestResourceToffoli:
+    """Test the ResourceToffoli operation"""
+
+    op = re.ResourceToffoli(wires=[0, 1, 2])
+
+    def test_resource_rep(self):
+        """Test the resource_rep produces the correct compressed representation."""
+        expected_rep = re.CompressedResourceOp(re.ResourceToffoli, {"elbow": None})
+        assert self.op.resource_rep(**self.op.resource_params) == expected_rep
+
+    def test_resource_params(self):
+        """Test that the resource_params are produced as expected."""
+        expected_params = {"elbow": None}
+        assert self.op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        expected_res = [re.GateCount(self.op.resource_rep(), 1)]
+
+        assert self.op.adjoint_resource_decomp() == expected_res
+
+    ctrl_data = (
+        (
+            ["c1"],
+            [1],
+            [re.GateCount(re.ResourceMultiControlledX.resource_rep(3, 0), 1)],
+        ),
+        (
+            ["c1", "c2"],
+            [1, 1],
+            [re.GateCount(re.ResourceMultiControlledX.resource_rep(4, 0), 1)],
+        ),
+        (
+            ["c1", "c2", "c3"],
+            [1, 0, 0],
+            [re.GateCount(re.ResourceMultiControlledX.resource_rep(5, 2), 1)],
+        ),
+    )
+
+    @pytest.mark.parametrize(
+        "ctrl_wires, ctrl_values, expected_res",
+        ctrl_data,
+    )
+    def test_resource_controlled(self, ctrl_wires, ctrl_values, expected_res):
+        """Test that the controlled resources are as expected"""
+        num_ctrl_wires = len(ctrl_wires)
+        num_ctrl_values = len([v for v in ctrl_values if not v])
+
+        assert self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values) == expected_res
+
+    pow_data = (
+        (1, [re.GateCount(op.resource_rep(), 1)]),
+        (2, [re.GateCount(re.ResourceIdentity.resource_rep(), 1)]),
+        (5, [re.GateCount(op.resource_rep(), 1)]),
+        (8, [re.GateCount(re.ResourceIdentity.resource_rep(), 1)]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+
+        assert self.op.pow_resource_decomp(z) == expected_res
+
+
+class TestResourceMultiControlledX:
+    """Test the ResourceMultiControlledX operation"""
+
+    res_ops = (
+        re.ResourceMultiControlledX(1, 0),
+        re.ResourceMultiControlledX(2, 0),
+        re.ResourceMultiControlledX(3, 0),
+        re.ResourceMultiControlledX(5, 0),
+        re.ResourceMultiControlledX(1, 1),
+        re.ResourceMultiControlledX(2, 1),
+        re.ResourceMultiControlledX(3, 2),
+        re.ResourceMultiControlledX(5, 3),
+    )
+
+    res_params = (
+        (1, 0),
+        (2, 0),
+        (3, 0),
+        (5, 0),
+        (1, 1),
+        (2, 1),
+        (3, 2),
+        (5, 3),
+    )
+
+    expected_resources = (
+        [re.GateCount(re.ResourceCNOT.resource_rep(), 1)],
+        [re.GateCount(re.ResourceToffoli.resource_rep(), 1)],
+        [
+            AllocWires(1),
+            re.GateCount(re.ResourceCNOT.resource_rep(), 2),
+            re.GateCount(re.ResourceToffoli.resource_rep(), 2),
+            FreeWires(1),
+        ],
+        [
+            AllocWires(3),
+            re.GateCount(re.resource_rep(re.ResourceTempAND), 3),
+            re.GateCount(re.resource_rep(re.ResourceToffoli, {"elbow": "right"}), 3),
+            re.GateCount(re.resource_rep(re.ResourceToffoli, {}), 1),
+            FreeWires(3),
+        ],
+        [
+            re.GateCount(re.ResourceX.resource_rep(), 2),
+            re.GateCount(re.ResourceCNOT.resource_rep(), 1),
+        ],
+        [
+            re.GateCount(re.ResourceX.resource_rep(), 2),
+            re.GateCount(re.ResourceToffoli.resource_rep(), 1),
+        ],
+        [
+            re.GateCount(re.resource_rep(re.ResourceX), 4),
+            AllocWires(1),
+            re.GateCount(re.resource_rep(re.ResourceCNOT), 2),
+            re.GateCount(re.resource_rep(re.ResourceToffoli), 2),
+            FreeWires(1),
+        ],
+        [
+            re.GateCount(re.resource_rep(re.ResourceX), 6),
+            AllocWires(3),
+            re.GateCount(re.resource_rep(re.ResourceTempAND), 3),
+            re.GateCount(re.resource_rep(re.ResourceToffoli, {"elbow": "right"}), 3),
+            re.GateCount(re.resource_rep(re.ResourceToffoli, {}), 1),
+            FreeWires(3),
+        ],
+    )
+
+    @staticmethod
+    def _prep_params(num_control, num_control_values):
+        return {
+            "num_ctrl_wires": num_control,
+            "num_ctrl_values": num_control_values,
+        }
+
+    @pytest.mark.parametrize("params, expected_res", zip(res_params, expected_resources))
+    def test_resources(self, params, expected_res):
+        """Test that the resources method produces the expected resources."""
+        op_resource_params = self._prep_params(*params)
+        assert repr(re.ResourceMultiControlledX.resource_decomp(**op_resource_params)) == repr(
+            expected_res
+        )
+
+    @pytest.mark.parametrize("op, params", zip(res_ops, res_params))
+    def test_resource_rep(self, op, params):
+        """Test the resource_rep produces the correct compressed representation."""
+        op_resource_params = self._prep_params(*params)
+        expected_rep = re.CompressedResourceOp(re.ResourceMultiControlledX, op_resource_params)
+        assert op.resource_rep(**op.resource_params) == expected_rep
+
+    @pytest.mark.parametrize("op, params", zip(res_ops, res_params))
+    def test_resource_params(self, op, params):
+        """Test that the resource_params are produced as expected."""
+        expected_params = self._prep_params(*params)
+        print("params", params, expected_params, op.resource_params)
+        assert op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        op = re.ResourceMultiControlledX(5, 3)
+
+        expected_res = [re.GateCount(op.resource_rep(**op.resource_params), 1)]
+
+        assert op.adjoint_resource_decomp(**op.resource_params) == expected_res
+
+    pow_data = (
+        (1, [re.GateCount(re.ResourceMultiControlledX.resource_rep(5, 3), 1)]),
+        (2, [re.GateCount(re.ResourceIdentity.resource_rep())]),
+        (5, [re.GateCount(re.ResourceMultiControlledX.resource_rep(5, 3), 1)]),
+        (6, [re.GateCount(re.ResourceIdentity.resource_rep())]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+        op = re.ResourceMultiControlledX(5, 3)
+
+        assert op.pow_resource_decomp(z, **op.resource_params) == expected_res
+
+
+class TestResourceCRX:
+    """Test the ResourceCRX operation"""
+
+    op = re.ResourceCRX(wires=[0, 1])
+
+    def test_resources(self):
+        """Test that the resources method produces the expected resources."""
+
+        expected_resources = [
+            re.GateCount(re.ResourceCNOT.resource_rep(), 2),
+            re.GateCount(re.ResourceRZ.resource_rep(), 2),
+            re.GateCount(re.ResourceHadamard.resource_rep(), 2),
+        ]
+        assert self.op.resource_decomp(**self.op.resource_params) == expected_resources
+
+    def test_resource_rep(self):
+        """Test the resource_rep produces the correct compressed representation."""
+        expected_rep = re.CompressedResourceOp(re.ResourceCRX, {"eps": None})
+        assert self.op.resource_rep(**self.op.resource_params) == expected_rep
+
+    def test_resource_params(self):
+        """Test that the resource_params are produced as expected."""
+        expected_params = {"eps": None}
+        assert self.op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        expected_res = [re.GateCount(self.op.resource_rep(), 1)]
+
+        assert self.op.adjoint_resource_decomp() == expected_res
+
+    pow_data = (
+        (1, [re.GateCount(op.resource_rep(), 1)]),
+        (2, [re.GateCount(op.resource_rep(), 1)]),
+        (5, [re.GateCount(op.resource_rep(), 1)]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+
+        assert self.op.pow_resource_decomp(z) == expected_res
+
+
+class TestResourceCRY:
+    """Test the ResourceCRY operation"""
+
+    op = re.ResourceCRY(wires=[0, 1])
+
+    def test_resources(self):
+        """Test that the resources method produces the expected resources."""
+
+        expected_resources = [
+            re.GateCount(re.ResourceCNOT.resource_rep(), 2),
+            re.GateCount(re.ResourceRY.resource_rep(), 2),
+        ]
+        assert self.op.resource_decomp(**self.op.resource_params) == expected_resources
+
+    def test_resource_rep(self):
+        """Test the resource_rep produces the correct compressed representation."""
+        expected_rep = re.CompressedResourceOp(re.ResourceCRY, {"eps": None})
+        assert self.op.resource_rep(**self.op.resource_params) == expected_rep
+
+    def test_resource_params(self):
+        """Test that the resource_params are produced as expected."""
+        expected_params = {"eps": None}
+        assert self.op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        expected_res = [re.GateCount(self.op.resource_rep(), 1)]
+
+        assert self.op.adjoint_resource_decomp() == expected_res
+
+    pow_data = (
+        (1, [re.GateCount(op.resource_rep(), 1)]),
+        (2, [re.GateCount(op.resource_rep(), 1)]),
+        (5, [re.GateCount(op.resource_rep(), 1)]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+
+        assert self.op.pow_resource_decomp(z) == expected_res
+
+
+class TestResourceCRZ:
+    """Test the ResourceCRZ operation"""
+
+    op = re.ResourceCRZ(wires=[0, 1])
+
+    def test_resources(self):
+        """Test that the resources method produces the expected resources."""
+
+        expected_resources = [
+            re.GateCount(re.ResourceCNOT.resource_rep(), 2),
+            re.GateCount(re.ResourceRZ.resource_rep(), 2),
+        ]
+        assert self.op.resource_decomp(**self.op.resource_params) == expected_resources
+
+    def test_resource_rep(self):
+        """Test the resource_rep produces the correct compressed representation."""
+        expected_rep = re.CompressedResourceOp(re.ResourceCRZ, {"eps": None})
+        assert self.op.resource_rep(**self.op.resource_params) == expected_rep
+
+    def test_resource_params(self):
+        """Test that the resource_params are produced as expected."""
+        expected_params = {"eps": None}
+        assert self.op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        expected_res = [re.GateCount(self.op.resource_rep(), 1)]
+
+        assert self.op.adjoint_resource_decomp() == expected_res
+
+    pow_data = (
+        (1, [re.GateCount(op.resource_rep(), 1)]),
+        (2, [re.GateCount(op.resource_rep(), 1)]),
+        (5, [re.GateCount(op.resource_rep(), 1)]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+
+        assert self.op.pow_resource_decomp(z) == expected_res
+
+
+class TestResourceCRot:
+    """Test the ResourceCRot operation"""
+
+    op = re.ResourceCRot(wires=[0, 1])
+
+    def test_resources(self):
+        """Test that the resources method produces the expected resources."""
+        expected_resources = [
+            re.GateCount(re.ResourceCNOT.resource_rep(), 2),
+            re.GateCount(re.ResourceRZ.resource_rep(), 3),
+            re.GateCount(re.ResourceRY.resource_rep(), 2),
+        ]
+        assert self.op.resource_decomp(**self.op.resource_params) == expected_resources
+
+    def test_resource_rep(self):
+        """Test the resource_rep produces the correct compressed representation."""
+        expected_rep = re.CompressedResourceOp(re.ResourceCRot, {"eps": None})
+        assert self.op.resource_rep(**self.op.resource_params) == expected_rep
+
+    def test_resource_params(self):
+        """Test that the resource_params are produced as expected."""
+        expected_params = {"eps": None}
+        assert self.op.resource_params == expected_params
+
+    def test_resource_adjoint(self):
+        """Test that the adjoint resources are as expected"""
+        expected_res = [re.GateCount(self.op.resource_rep(), 1)]
+
+        assert self.op.adjoint_resource_decomp() == expected_res
+
+    pow_data = (
+        (1, [re.GateCount(op.resource_rep(), 1)]),
+        (2, [re.GateCount(op.resource_rep(), 1)]),
+        (5, [re.GateCount(op.resource_rep(), 1)]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_resource_pow(self, z, expected_res):
+        """Test that the pow resources are as expected"""
+
+        assert self.op.pow_resource_decomp(z) == expected_res
+
+
+class TestResourceControlledPhaseShift:
+    """Test ResourceControlledPhaseShift"""
+
+    def test_resources(self):
+        """Test the resources method"""
+
+        op = re.ResourceControlledPhaseShift()
+
+        expected = [
+            re.GateCount(re.CompressedResourceOp(re.ResourceCNOT, {}), 2),
+            re.GateCount(re.CompressedResourceOp(re.ResourceRZ, {"eps": None}), 3),
+        ]
+
+        assert op.resource_decomp(**op.resource_params) == expected
+
+    def test_resource_params(self):
+        """Test the resource parameters"""
+
+        op = re.ResourceControlledPhaseShift()
+        assert op.resource_params == {
+            "eps": None
+        }  # pylint: disable=use-implicit-booleaness-not-comparison
+
+    def test_resource_rep(self):
+        """Test the compressed representation"""
+
+        op = re.ResourceControlledPhaseShift()
+        expected = re.CompressedResourceOp(re.ResourceControlledPhaseShift, {"eps": None})
+
+        assert op.resource_rep() == expected
+
+    def test_resource_rep_from_op(self):
+        """Test resource_rep_from_op method"""
+
+        op = re.ResourceControlledPhaseShift()
+        assert op.resource_rep_from_op() == re.ResourceControlledPhaseShift.resource_rep(
+            **op.resource_params
+        )
+
+    def test_resources_from_rep(self):
+        """Compute the resources from the compressed representation"""
+
+        op = re.ResourceControlledPhaseShift()
+
+        expected = [
+            re.GateCount(re.CompressedResourceOp(re.ResourceCNOT, {}), 2),
+            re.GateCount(re.CompressedResourceOp(re.ResourceRZ, {"eps": None}), 3),
+        ]
+
+        op_compressed_rep = op.resource_rep_from_op()
+        op_resource_params = op_compressed_rep.params
+        op_compressed_rep_type = op_compressed_rep.op_type
+
+        assert op_compressed_rep_type.resource_decomp(**op_resource_params) == expected
+
+    def test_adjoint_decomp(self):
+        """Test that the adjoint resources are correct."""
+
+        op = re.ResourceControlledPhaseShift()
+
+        assert op.default_adjoint_resource_decomp() == [
+            re.GateCount(re.ResourceControlledPhaseShift.resource_rep(), 1)
+        ]
+
+    pow_data = ((1, [re.GateCount(re.ResourceControlledPhaseShift.resource_rep(), 1)]),)
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_pow_decomp(self, z, expected_res):
+        """Test that the adjoint resources are correct."""
+
+        op = re.ResourceControlledPhaseShift
+
+        assert op.default_pow_resource_decomp(z) == expected_res
diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py
index 3c5d8f71c..d19e9d6a2 100644
--- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py
+++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py
@@ -62,6 +62,106 @@ class TestHadamard:
         assert op.pow_resource_decomp(z) == expected_res
 
 
+class TestSWAP:
+    """Tests for ResourceSWAP"""
+
+    def test_resources(self):
+        """Test that SWAP decomposes into three CNOTs"""
+        op = plre.ResourceSWAP([0, 1])
+        cnot = plre.ResourceCNOT.resource_rep()
+        expected = [plre.GateCount(cnot, 3)]
+
+        assert op.resource_decomp() == expected
+
+    def test_resource_params(self):
+        """Test that the resource params are correct"""
+        op = plre.ResourceSWAP([0, 1])
+        assert op.resource_params == {}
+
+    def test_resource_rep(self):
+        """Test the compact representation"""
+        expected = plre.CompressedResourceOp(plre.ResourceSWAP, {})
+        assert plre.ResourceSWAP.resource_rep() == expected
+
+    def test_resources_from_rep(self):
+        """Test that the resources can be computed from the compressed representation"""
+
+        op = plre.ResourceSWAP([0, 1])
+        expected = [plre.GateCount(plre.ResourceCNOT.resource_rep(), 3)]
+
+        op_compressed_rep = op.resource_rep_from_op()
+        op_resource_type = op_compressed_rep.op_type
+        op_resource_params = op_compressed_rep.params
+        assert op_resource_type.resource_decomp(**op_resource_params) == expected
+
+    def test_adjoint_decomp(self):
+        """Test that the adjoint decomposition is correct."""
+        swap = plre.ResourceSWAP([0, 1])
+        expected = [plre.GateCount(swap.resource_rep(), 1)]
+
+        assert swap.adjoint_resource_decomp() == expected
+
+    ctrl_data = (
+        (
+            ["c1"],
+            [1],
+            [
+                plre.GateCount(plre.ResourceCSWAP.resource_rep(), 1),
+            ],
+        ),
+        (
+            ["c1"],
+            [0],
+            [
+                plre.GateCount(plre.ResourceCSWAP.resource_rep(), 1),
+                plre.GateCount(plre.ResourceX.resource_rep(), 2),
+            ],
+        ),
+        (
+            ["c1", "c2"],
+            [1, 1],
+            [
+                plre.GateCount(plre.ResourceCNOT.resource_rep(), 2),
+                plre.GateCount(plre.ResourceMultiControlledX.resource_rep(2, 0), 1),
+            ],
+        ),
+        (
+            ["c1", "c2", "c3"],
+            [1, 0, 0],
+            [
+                plre.GateCount(plre.ResourceCNOT.resource_rep(), 2),
+                plre.GateCount(plre.ResourceMultiControlledX.resource_rep(3, 2), 1),
+            ],
+        ),
+    )
+
+    @pytest.mark.parametrize(
+        "ctrl_wires, ctrl_values, expected_res",
+        ctrl_data,
+    )
+    def test_resource_controlled(self, ctrl_wires, ctrl_values, expected_res):
+        """Test that the controlled resources are as expected"""
+        num_ctrl_wires = len(ctrl_wires)
+        num_ctrl_values = len([v for v in ctrl_values if not v])
+
+        op = plre.ResourceSWAP([0, 1])
+
+        assert op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values) == expected_res
+
+    pow_data = (
+        (1, [plre.GateCount(plre.ResourceSWAP.resource_rep(), 1)]),
+        (2, [plre.GateCount(plre.ResourceIdentity.resource_rep(), 1)]),
+        (3, [plre.GateCount(plre.ResourceSWAP.resource_rep(), 1)]),
+        (4, [plre.GateCount(plre.ResourceIdentity.resource_rep(), 1)]),
+    )
+
+    @pytest.mark.parametrize("z, expected_res", pow_data)
+    def test_pow_decomp(self, z, expected_res):
+        """Test that the pow decomposition is correct."""
+        op = plre.ResourceSWAP([0, 1])
+        assert op.pow_resource_decomp(z) == expected_res
+
+
 class TestS:
     """Tests for ResourceS"""