Repository: PennyLaneAI/pennylane
Pull request #7705 (2025-06-17 21:38:53)

Add resource templates for trotterization of CDF and THC Hamiltonians
**Context:**
Add resource templates for trotterization of compact Hamiltonian classes.

**Description of the Change:**

**Benefits:**

**Possible Drawbacks:**

**Related GitHub Issues:**


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 331fd8d79..4c390a1b4 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -792,6 +792,12 @@
   estimation for multi-qubit parametic gates.
   [(#7549)](https://github.com/PennyLaneAI/pennylane/pull/7549)
 
+* Added the `pennylane.labs.resource_estimation.CompactHamiltonian` to unblock the need to pass full
+  Hamiltonian
+  Added `pennylane.labs.resource_estimation.ResourceTrotterCDF`, and `pennylane.labs.resource_estimation.ResourceTrotterTHC`
+  templates which will be used to perform resource estimation for trotterization of CDF and THC Hamiltonians respectively.
+  [(#7705)](https://github.com/PennyLaneAI/pennylane/pull/7705)
+
 * Added `pennylane.labs.ResourceOperator` templates for various algorithms required for 
   supporting compact hamiltonian development.
   [(#7725)](https://github.com/PennyLaneAI/pennylane/pull/7725)
@@ -1042,17 +1048,14 @@ Here's a list of deprecations made this release. For a more detailed breakdown o
   disabling program capture.
   [(#7298)](https://github.com/PennyLaneAI/pennylane/pull/7298)
 
-<<<<<<< snapshots-warning
 * Added a warning to the documentation for `qml.snapshots` and `qml.Snapshot`, clarifying that compilation transforms 
 may move operations across a `Snapshot`.
   [(#7746)](https://github.com/PennyLaneAI/pennylane/pull/7746)
-=======
+
 * In the :doc:`/development/guide/documentation` page, removed references to the outdated Sphinx and unsupported Python 3.8 version. 
   This helps ensure contributors follow current standards and avoid compatibility issues.
   [(#7479)](https://github.com/PennyLaneAI/pennylane/pull/7479)
 
->>>>>>> master
-
 <h3>Bug fixes 🐛</h3>
 
 * Fixes a bug with transforms that require the classical Jacobian applied to QNodes, where only
diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py
index c5dfc1d00..04bab07d4 100644
--- a/pennylane/labs/resource_estimation/__init__.py
+++ b/pennylane/labs/resource_estimation/__init__.py
@@ -125,6 +125,16 @@ Templates:
     ~ResourceBasisRotation
     ~ResourceSelect
     ~ResourceQROM
+    ~ResourceTrotterCDF
+    ~ResourceTrotterTHC
+
+Compact Hamiltonian Class:
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autosummary::
+    :toctree: api
+
+    ~CompactHamiltonian
 
 """
 
@@ -191,7 +201,6 @@ from .ops import (
     ResourceChangeBasisOp,
     ResourcePow,
 )
-
 from .templates import (
     ResourceOutOfPlaceSquare,
     ResourcePhaseGradient,
@@ -200,4 +209,7 @@ from .templates import (
     ResourceBasisRotation,
     ResourceSelect,
     ResourceQROM,
+    ResourceTrotterCDF,
+    ResourceTrotterTHC,
+    CompactHamiltonian,
 )
diff --git a/pennylane/labs/resource_estimation/templates/__init__.py b/pennylane/labs/resource_estimation/templates/__init__.py
index 8a7f28ed3..d81177cf9 100644
--- a/pennylane/labs/resource_estimation/templates/__init__.py
+++ b/pennylane/labs/resource_estimation/templates/__init__.py
@@ -22,3 +22,5 @@ from .subroutines import (
     ResourceSelect,
     ResourceQROM,
 )
+from .compact_hamiltonian import CompactHamiltonian
+from .trotter import ResourceTrotterCDF, ResourceTrotterTHC
diff --git a/pennylane/labs/resource_estimation/templates/compact_hamiltonian.py b/pennylane/labs/resource_estimation/templates/compact_hamiltonian.py
new file mode 100644
index 000000000..100bbf397
--- /dev/null
+++ b/pennylane/labs/resource_estimation/templates/compact_hamiltonian.py
@@ -0,0 +1,83 @@
+# 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.
+"""
+Contains class to represent different Hamiltonians.
+"""
+
+from typing import Any, Dict
+
+
+class CompactHamiltonian:
+    r"""A compact representation for the Hamiltonian of a quantum system.
+
+    Args:
+        method_name (str): The name of the method used to construct the Hamiltonian
+            The available are cdf, and thc.
+        **params (Any): Keyword arguments specific to the chosen construction method,
+
+            - For :meth:`~.CompactHamiltonian.cdf`, parameters include ``num_orbitals`` and ``num_fragments``.
+            - For :meth:`~.CompactHamiltonian.thc`, parameters include ``num_orbitals`` and ``tensor_rank``.
+
+
+    Returns:
+        CompactHamiltonian: An instance of CompactHamiltonian
+
+    **Example**
+
+    The resources for trotterization of THC Hamiltonian can be extracted as:
+
+    >>> import pennylane.labs.resource_estimation as plre
+    >>> compact_ham = plre.CompactHamiltonian.thc(num_orbitals=8, tensor_rank=40)
+    >>> trotter_thc = plre.ResourceTrotterTHC(compact_ham, num_steps=100, order=2)
+    >>> res = plre.estimate_resources(trotter_thc)
+    >>> print(res)
+    --- Resources: ---
+     Total qubits: 80
+     Total gates : 3.960E+7
+     Qubit breakdown:
+      clean qubits: 0, dirty qubits: 0, algorithmic qubits: 80
+     Gate breakdown:
+      {'T': 3.638E+7, 'S': 9.699E+5, 'Z': 6.466E+5, 'Hadamard': 6.466E+5, 'CNOT': 9.553E+5}
+
+    """
+
+    def __init__(self, method_name: str, **params: Dict[str, Any]):
+        self.method_name = method_name
+        self.params = params
+
+    @classmethod
+    def cdf(cls, num_orbitals: int, num_fragments: int):
+        """Constructs a compressed double factorized Hamiltonian instance
+
+        Args:
+            num_orbitals (int): number of spatial orbitals
+            num_fragments (int): number of fragments in the compressed double factorization (CDF) representation
+
+        Returns:
+            CompactHamiltonian: An instance of CompactHamiltonian initialized with CDF parameters.
+        """
+        return cls("cdf", num_orbitals=num_orbitals, num_fragments=num_fragments)
+
+    @classmethod
+    def thc(cls, num_orbitals: int, tensor_rank: int):
+        """Constructs a tensor hypercontracted Hamiltonian instance
+
+        Args:
+            num_orbitals (int): number of spatial orbitals
+            tensor_rank (int):  tensor rank of two-body integrals in the tensor hypercontracted (THC) representation
+
+        Returns:
+            CompactHamiltonian: An instance of CompactHamiltonian initialized with THC parameters.
+        """
+        return cls("thc", num_orbitals=num_orbitals, tensor_rank=tensor_rank)
diff --git a/pennylane/labs/resource_estimation/templates/trotter.py b/pennylane/labs/resource_estimation/templates/trotter.py
new file mode 100644
index 000000000..17059249f
--- /dev/null
+++ b/pennylane/labs/resource_estimation/templates/trotter.py
@@ -0,0 +1,582 @@
+# 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.
+"""
+Contains templates for Suzuki-Trotter approximation based subroutines.
+"""
+from pennylane.labs import resource_estimation as plre
+from pennylane.labs.resource_estimation.resource_operator import (
+    CompressedResourceOp,
+    GateCount,
+    ResourceOperator,
+    resource_rep,
+)
+from pennylane.wires import Wires
+
+# pylint: disable=arguments-differ, too-many-arguments
+
+
+class ResourceTrotterCDF(ResourceOperator):  # pylint: disable=too-many-ancestors
+    r"""An operation representing the Suzuki-Trotter product approximation for the complex matrix
+    exponential of compressed double factorized Hamiltonian.
+
+    The Suzuki-Trotter product formula provides a method to approximate the matrix exponential of
+    Hamiltonian expressed as a linear combination of terms which in general do not commute. Consider
+    the Hamiltonian :math:`H = \Sigma^{N}_{j=0} O_{j}`, the product formula is constructed using
+    symmetrized products of the terms in the Hamiltonian. The symmetrized products of order
+    :math:`m \in [1, 2, 4, ..., 2k]` with :math:`k \in \mathbb{N}` are given by:
+
+    .. math::
+
+        \begin{align}
+            S_{1}(t) &= \Pi_{j=0}^{N} \ e^{i t O_{j}} \\
+            S_{2}(t) &= \Pi_{j=0}^{N} \ e^{i \frac{t}{2} O_{j}} \cdot \Pi_{j=N}^{0} \ e^{i \frac{t}{2} O_{j}} \\
+            &\vdots \\
+            S_{m}(t) &= S_{m-2}(p_{m}t)^{2} \cdot S_{m-2}((1-4p_{m})t) \cdot S_{m-2}(p_{m}t)^{2},
+        \end{align}
+
+    where the coefficient is :math:`p_{m} = 1 / (4 - \sqrt[m - 1]{4})`. The :math:`m^{\text{th}}` order,
+    :math:`n`-step Suzuki-Trotter approximation is then defined as:
+
+    .. math:: e^{iHt} \approx \left [S_{m}(t / n)  \right ]^{n}.
+
+    For more details see `J. Math. Phys. 32, 400 (1991) <https://pubs.aip.org/aip/jmp/article-abstract/32/2/400/229229>`_.
+
+    Args:
+        compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a compressed double factorized
+            Hamiltonian to be approximately exponentiated
+        num_steps (int): number of Trotter steps to perform
+        order (int): order of the approximation, must be 1 or even.
+        wires (list[int] or optional): the wires on which the operator acts
+
+    Resources:
+        The resources are defined according to the recursive formula presented above. Specifically, each
+        operator in a single step expansion of the exponentiation is called a number of times given by the formula:
+
+        .. math:: C_{O_{j}} = 2n \cdot 5^{\frac{m}{2} - 1}
+
+        Furthermore, the first and last terms of the Hamiltonian appear in pairs due to the symmetric form
+        of the recursive formula. Those counts are further simplified by grouping like terms as:
+
+        .. math::
+
+            \begin{align}
+                C_{O_{0}} &= n \cdot 5^{\frac{m}{2} - 1} + 1,  \\
+                C_{O_{N}} &= n \cdot 5^{\frac{m}{2} - 1}.
+            \end{align}
+
+        The resources for a single step expansion of compressed double factorized Hamiltonian are calculated
+        based on `arXiv:2506.15784 <https://arxiv.org/abs/2506.15784>`_.
+
+
+    .. seealso:: :class:`~.TrotterProduct`
+
+    The resources can be computed as:
+
+    **Example**
+
+    >>> import pennylane.labs.resource_estimation as plre
+    >>> num_steps, order = (1, 2)
+    >>> compact_ham = plre.CompactHamiltonian.cdf(num_orbitals = 4, num_fragments = 4)
+    >>> res = plre.estimate_resources(plre.ResourceTrotterCDF(compact_ham, num_steps, order))
+    >>> print(res)
+    --- Resources: ---
+     Total qubits: 8
+     Total gates : 2.238E+4
+     Qubit breakdown:
+      clean qubits: 0, dirty qubits: 0, algorithmic qubits: 8
+     Gate breakdown:
+      {'T': 2.075E+4, 'S': 504.0, 'Z': 336.0, 'Hadamard': 336.0, 'CNOT': 448.0}
+    """
+
+    resource_keys = {"compact_ham", "num_steps", "order"}
+
+    def __init__(self, compact_ham, num_steps, order, wires=None):
+
+        if compact_ham.method_name != "cdf":
+            raise TypeError(
+                f"Unsupported Hamiltonian representation for ResourceTrotterCDF."
+                f"This method works with cdf Hamiltonian, {compact_ham.method_name} provided"
+            )
+        self.num_steps = num_steps
+        self.order = order
+        self.compact_ham = compact_ham
+
+        if wires is not None:
+            self.wires = Wires(wires)
+            self.num_wires = len(self.wires)
+        else:
+            self.wires = None
+            self.num_wires = 2 * compact_ham.params["num_orbitals"]
+        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:
+                * compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a compressed double factorized
+                  Hamiltonian to be approximately exponentiated
+                * num_steps (int): number of Trotter steps to perform
+                * order (int): order of the approximation, must be 1 or even.
+
+        """
+        return {
+            "compact_ham": self.compact_ham,
+            "num_steps": self.num_steps,
+            "order": self.order,
+        }
+
+    @classmethod
+    def resource_rep(cls, compact_ham, num_steps, order) -> CompressedResourceOp:
+        """Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute a resource estimation.
+
+        Args:
+            compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a compressed double factorized
+                Hamiltonian to be approximately exponentiated
+            num_steps (int): number of Trotter steps to perform
+            order (int): order of the approximation, must be 1 or even.
+
+        Returns:
+            CompressedResourceOp: the operator in a compressed representation
+        """
+        params = {
+            "compact_ham": compact_ham,
+            "num_steps": num_steps,
+            "order": order,
+        }
+        return CompressedResourceOp(cls, params)
+
+    @classmethod
+    def default_resource_decomp(cls, compact_ham, num_steps, order, **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.
+
+        Args:
+            compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a compressed double factorized
+                Hamiltonian to be approximately exponentiated
+            num_steps (int): number of Trotter steps to perform
+            order (int): order of the approximation, must be 1 or even.
+
+        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
+
+        """
+        k = order // 2
+        gate_list = []
+        num_orb = compact_ham.params["num_orbitals"]
+        num_frags = compact_ham.params["num_fragments"]
+
+        op_onebody = resource_rep(
+            plre.ResourceProd,
+            {"cmpr_factors_and_counts": ((plre.ResourceRZ.resource_rep(), 2 * num_orb),)},
+        )
+
+        op_twobody = resource_rep(
+            plre.ResourceProd,
+            {
+                "cmpr_factors_and_counts": (
+                    (plre.ResourceMultiRZ.resource_rep(num_wires=2), (2 * num_orb - 1) * num_orb),
+                )
+            },
+        )
+
+        basis_rot = resource_rep(plre.ResourceBasisRotation, {"dim_N": num_orb})
+
+        if order == 1:
+            gate_list.append(plre.GateCount(basis_rot, 2 * num_frags * num_steps))
+
+            gate_list.append(plre.GateCount(op_onebody, num_steps))
+            gate_list.append(plre.GateCount(op_twobody, (num_frags - 1) * num_steps))
+            return gate_list
+
+        # For first and last fragment
+        gate_list.append(plre.GateCount(basis_rot, 4 * num_steps * (5 ** (k - 1)) + 2))
+        gate_list.append(plre.GateCount(op_onebody, num_steps * (5 ** (k - 1)) + 1))
+        gate_list.append(plre.GateCount(op_twobody, num_steps * (5 ** (k - 1))))
+
+        # For rest of the fragments
+        gate_list.append(
+            plre.GateCount(basis_rot, 4 * num_steps * (num_frags - 2) * (5 ** (k - 1)))
+        )
+        gate_list.append(
+            plre.GateCount(op_twobody, 2 * num_steps * (num_frags - 2) * (5 ** (k - 1)))
+        )
+
+        return gate_list
+
+    @classmethod
+    def default_controlled_resource_decomp(
+        cls, compact_ham, num_steps, order, ctrl_num_ctrl_wires, ctrl_num_ctrl_values, **kwargs
+    ):
+        """Returns the controlled resource decomposition.
+
+        Args:
+            compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a compressed double factorized
+                Hamiltonian to be approximately exponentiated
+            num_steps (int): number of Trotter steps to perform
+            order (int): order of the approximation, must be 1 or even.
+            ctrl_num_ctrl_wires (int): the number of control wires for the controlled operations
+            ctrl_num_ctrl_values (int): the number of control values for the controlled operations
+
+        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.
+
+        Resources:
+            The original resources are controlled only on the Z rotation gates.
+
+        """
+        k = order // 2
+        gate_list = []
+        num_orb = compact_ham.params["num_orbitals"]
+        num_frags = compact_ham.params["num_fragments"]
+
+        op_onebody = resource_rep(
+            plre.ResourceProd,
+            {
+                "cmpr_factors_and_counts": tuple(
+                    resource_rep(
+                        plre.ResourceControlled,
+                        {
+                            "base_cmpr_op": plre.ResourceRZ.resource_rep(),
+                            "num_ctrl_wires": ctrl_num_ctrl_wires,
+                            "num_ctrl_values": ctrl_num_ctrl_values,
+                        },
+                    ),
+                    (2 * num_orb),
+                )
+            },
+        )
+
+        op_twobody = resource_rep(
+            plre.ResourceProd,
+            {
+                "cmpr_factors_and_counts": tuple(
+                    resource_rep(
+                        plre.ResourceControlled,
+                        {
+                            "base_cmpr_op": plre.ResourceMultiRZ.resource_rep(num_wires=2),
+                            "num_ctrl_wires": ctrl_num_ctrl_wires,
+                            "num_ctrl_values": ctrl_num_ctrl_values,
+                        },
+                    ),
+                    (2 * num_orb - 1) * num_orb,
+                )
+            },
+        )
+
+        basis_rot = resource_rep(plre.ResourceBasisRotation, {"dim_N": num_orb})
+
+        if order == 1:
+            print("Basis rot: ", num_frags * num_steps)
+            gate_list.append(plre.GateCount(basis_rot, 2 * num_frags * num_steps))
+
+            gate_list.append(plre.GateCount(op_onebody, num_steps))
+            gate_list.append(plre.GateCount(op_twobody, (num_frags - 1) * num_steps))
+            return gate_list
+
+        # For first and last fragment
+        gate_list.append(plre.GateCount(basis_rot, 4 * num_steps * (5 ** (k - 1)) + 2))
+        gate_list.append(plre.GateCount(op_onebody, num_steps * (5 ** (k - 1)) + 1))
+        gate_list.append(plre.GateCount(op_twobody, num_steps * (5 ** (k - 1))))
+
+        # For rest of the fragments
+        gate_list.append(
+            plre.GateCount(basis_rot, 4 * num_steps * (num_frags - 2) * (5 ** (k - 1)))
+        )
+        gate_list.append(
+            plre.GateCount(op_twobody, 2 * num_steps * (num_frags - 2) * (5 ** (k - 1)))
+        )
+
+        return gate_list
+
+
+class ResourceTrotterTHC(ResourceOperator):  # pylint: disable=too-many-ancestors
+    r"""An operation representing the Suzuki-Trotter product approximation for the complex matrix
+    exponential of tensor hypercontracted Hamiltonian.
+
+    The Suzuki-Trotter product formula provides a method to approximate the matrix exponential of
+    Hamiltonian expressed as a linear combination of terms which in general do not commute. Consider
+    the Hamiltonian :math:`H = \Sigma^{N}_{j=0} O_{j}`, the product formula is constructed using
+    symmetrized products of the terms in the Hamiltonian. The symmetrized products of order
+    :math:`m \in [1, 2, 4, ..., 2k]` with :math:`k \in \mathbb{N}` are given by:
+
+    .. math::
+
+        \begin{align}
+            S_{1}(t) &= \Pi_{j=0}^{N} \ e^{i t O_{j}} \\
+            S_{2}(t) &= \Pi_{j=0}^{N} \ e^{i \frac{t}{2} O_{j}} \cdot \Pi_{j=N}^{0} \ e^{i \frac{t}{2} O_{j}} \\
+            &\vdots \\
+            S_{m}(t) &= S_{m-2}(p_{m}t)^{2} \cdot S_{m-2}((1-4p_{m})t) \cdot S_{m-2}(p_{m}t)^{2},
+        \end{align}
+
+    where the coefficient is :math:`p_{m} = 1 / (4 - \sqrt[m - 1]{4})`. The :math:`m^{\text{th}}` order,
+    :math:`n`-step Suzuki-Trotter approximation is then defined as:
+
+    .. math:: e^{iHt} \approx \left [S_{m}(t / n)  \right ]^{n}.
+
+    For more details see `J. Math. Phys. 32, 400 (1991) <https://pubs.aip.org/aip/jmp/article-abstract/32/2/400/229229>`_.
+
+    Args:
+        compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted
+                Hamiltonian to be approximately exponentiated
+        num_steps (int): number of Trotter steps to perform
+        order (int): order of the approximation, must be 1 or even
+        wires (list[int] or optional): the wires on which the operator acts
+
+    Resources:
+        The resources are defined according to the recursive formula presented above. Specifically, each
+        operator in the single step expansion of the exponentiation is called a number of times given by the formula:
+
+        .. math:: C_{O_{j}} = 2n \cdot 5^{\frac{m}{2} - 1}
+
+        Furthermore, the first and last terms of the Hamiltonian appear in pairs due to the symmetric form
+        of the recursive formula. Those counts are further simplified by grouping like terms as:
+
+        .. math::
+
+            \begin{align}
+                C_{O_{0}} &= n \cdot 5^{\frac{m}{2} - 1} + 1,  \\
+                C_{O_{N}} &= n \cdot 5^{\frac{m}{2} - 1}.
+            \end{align}
+
+        The resources for a single step expansion of tensor hypercontracted Hamiltonian are calculated
+        based on `arXiv:2407.04432 <https://arxiv.org/abs/2407.04432>`_
+
+    .. seealso:: :class:`~.TrotterProduct`
+
+    **Example**
+
+    The resources can be computed as:
+
+    >>> import pennylane.labs.resource_estimation as plre
+    >>> num_steps, order = (1, 2)
+    >>> compact_ham = plre.CompactHamiltonian.thc(num_orbitals=4, tensor_rank=4)
+    >>> res = plre.estimate_resources(plre.ResourceTrotterTHC(compact_ham, num_steps, order))
+    >>> print(res)
+    --- Resources: ---
+     Total qubits: 8
+     Total gates : 8.520E+3
+     Qubit breakdown:
+      clean qubits: 0, dirty qubits: 0, algorithmic qubits: 8
+     Gate breakdown:
+      {'T': 7.888E+3, 'S': 216.0, 'Z': 144.0, 'Hadamard': 144.0, 'CNOT': 128.0}
+
+    """
+
+    resource_keys = {"compact_ham", "num_steps", "order"}
+
+    def __init__(self, compact_ham, num_steps, order, wires=None):
+
+        if compact_ham.method_name != "thc":
+            raise TypeError(
+                f"Unsupported Hamiltonian representation for ResourceTrotterTHC."
+                f"This method works with thc Hamiltonian, {compact_ham.method_name} provided"
+            )
+        self.num_steps = num_steps
+        self.order = order
+        self.compact_ham = compact_ham
+
+        if wires is not None:
+            self.wires = Wires(wires)
+            self.num_wires = len(self.wires)
+        else:
+            self.wires = None
+            self.num_wires = compact_ham.params["tensor_rank"] * 2
+        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:
+                * compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted
+                  Hamiltonian to be approximately exponentiated
+                * num_steps (int): number of Trotter steps to perform
+                * order (int): order of the approximation, must be 1 or even
+
+        """
+        return {
+            "compact_ham": self.compact_ham,
+            "num_steps": self.num_steps,
+            "order": self.order,
+        }
+
+    @classmethod
+    def resource_rep(cls, compact_ham, num_steps, order) -> CompressedResourceOp:
+        """Returns a compressed representation containing only the parameters of
+        the Operator that are needed to compute the resources.
+
+        Args:
+            compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted
+                Hamiltonian to be approximately exponentiated
+            num_steps (int): number of Trotter steps to perform
+            order (int): order of the approximation, must be 1 or even
+
+        Returns:
+            CompressedResourceOp: the operator in a compressed representation
+        """
+        params = {
+            "compact_ham": compact_ham,
+            "num_steps": num_steps,
+            "order": order,
+        }
+        return CompressedResourceOp(cls, params)
+
+    @classmethod
+    def default_resource_decomp(cls, compact_ham, num_steps, order, **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.
+
+        Args:
+            compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted
+                Hamiltonian to be approximately exponentiated
+            num_steps (int): number of Trotter steps to perform
+            order (int): order of the approximation, must be 1 or even
+
+        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
+
+        """
+        k = order // 2
+        gate_list = []
+        num_orb = compact_ham.params["num_orbitals"]
+        tensor_rank = compact_ham.params["tensor_rank"]
+
+        op_onebody = resource_rep(
+            plre.ResourceProd,
+            {"cmpr_factors_and_counts": ((plre.ResourceRZ.resource_rep(), 2 * num_orb),)},
+        )
+
+        op_twobody = resource_rep(
+            plre.ResourceProd,
+            {
+                "cmpr_factors_and_counts": (
+                    (
+                        plre.ResourceMultiRZ.resource_rep(num_wires=2),
+                        (2 * tensor_rank - 1) * tensor_rank,
+                    ),
+                )
+            },
+        )
+
+        basis_rot_onebody = resource_rep(plre.ResourceBasisRotation, {"dim_N": num_orb})
+        basis_rot_twobody = resource_rep(plre.ResourceBasisRotation, {"dim_N": tensor_rank})
+
+        if order == 1:
+            gate_list.append(plre.GateCount(basis_rot_onebody, 2 * num_steps))
+            gate_list.append(plre.GateCount(basis_rot_twobody, 2 * num_steps))
+            gate_list.append(plre.GateCount(op_onebody, num_steps))
+            gate_list.append(plre.GateCount(op_twobody, num_steps))
+            return gate_list
+
+        # For one-body tensor
+        gate_list.append(plre.GateCount(basis_rot_onebody, 2 * num_steps * (5 ** (k - 1)) + 2))
+        gate_list.append(plre.GateCount(op_onebody, num_steps * (5 ** (k - 1)) + 1))
+
+        # For two-body tensor
+        gate_list.append(plre.GateCount(basis_rot_twobody, 2 * num_steps * (5 ** (k - 1))))
+        gate_list.append(plre.GateCount(op_twobody, num_steps * (5 ** (k - 1))))
+
+        return gate_list
+
+    @classmethod
+    def default_controlled_resource_decomp(
+        cls, compact_ham, num_steps, order, ctrl_num_ctrl_wires, ctrl_num_ctrl_values, **kwargs
+    ):
+        """Returns the controlled resource decomposition.
+
+        Args:
+            compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted
+                Hamiltonian to be approximately exponentiated
+            num_steps (int): number of Trotter steps to perform
+            order (int): order of the approximation, must be 1 or even
+            ctrl_num_ctrl_wires (int): the number of control wires for the controlled operations
+            ctrl_num_ctrl_values (int): the number of control values for the controlled operations
+
+        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.
+
+        Resources:
+            The original resources are controlled only on the Z rotation gates
+
+        """
+        k = order // 2
+        gate_list = []
+        num_orb = compact_ham.params["num_orbitals"]
+        tensor_rank = compact_ham.params["tensor_rank"]
+
+        op_onebody = resource_rep(
+            plre.ResourceProd,
+            {
+                "cmpr_factors_and_counts": tuple(
+                    resource_rep(
+                        plre.ResourceControlled,
+                        {
+                            "base_cmpr_op": plre.ResourceRZ.resource_rep(),
+                            "num_ctrl_wires": ctrl_num_ctrl_wires,
+                            "num_ctrl_values": ctrl_num_ctrl_values,
+                        },
+                    ),
+                    (2 * num_orb),
+                )
+            },
+        )
+
+        op_twobody = resource_rep(
+            plre.ResourceProd,
+            {
+                "cmpr_factors_and_counts": tuple(
+                    resource_rep(
+                        plre.ResourceControlled,
+                        {
+                            "base_cmpr_op": plre.ResourceMultiRZ.resource_rep(num_wires=2),
+                            "num_ctrl_wires": ctrl_num_ctrl_wires,
+                            "num_ctrl_values": ctrl_num_ctrl_values,
+                        },
+                    ),
+                    (2 * tensor_rank - 1) * tensor_rank,
+                )
+            },
+        )
+
+        basis_rot_onebody = resource_rep(plre.ResourceBasisRotation, {"dim_N": num_orb})
+        basis_rot_twobody = resource_rep(plre.ResourceBasisRotation, {"dim_N": tensor_rank})
+
+        if order == 1:
+            gate_list.append(plre.GateCount(basis_rot_onebody, 2 * num_steps))
+            gate_list.append(plre.GateCount(basis_rot_twobody, 2 * num_steps))
+            gate_list.append(plre.GateCount(op_onebody, num_steps))
+            gate_list.append(plre.GateCount(op_twobody, num_steps))
+            return gate_list
+
+        # For one-body tensor
+        gate_list.append(plre.GateCount(basis_rot_onebody, 2 * num_steps * (5 ** (k - 1)) + 2))
+        gate_list.append(plre.GateCount(op_onebody, num_steps * (5 ** (k - 1)) + 1))
+
+        # For two-body tensor
+        gate_list.append(plre.GateCount(basis_rot_twobody, 2 * num_steps * (5 ** (k - 1))))
+        gate_list.append(plre.GateCount(op_twobody, num_steps * (5 ** (k - 1))))
+
+        return gate_list


Test patch:
diff --git a/pennylane/labs/tests/resource_estimation/templates/test_trotter.py b/pennylane/labs/tests/resource_estimation/templates/test_trotter.py
new file mode 100644
index 000000000..024412600
--- /dev/null
+++ b/pennylane/labs/tests/resource_estimation/templates/test_trotter.py
@@ -0,0 +1,201 @@
+# 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.
+"""
+Test the Resource classes for Trotterization
+"""
+from collections import defaultdict
+
+import pytest
+
+import pennylane.labs.resource_estimation as plre
+from pennylane.labs.resource_estimation import QubitManager
+
+# pylint: disable=no-self-use, too-many-arguments, too-many-positional-arguments
+
+
+class TestTrotterCDF:
+    """Tests for ResourceTrotterCDF class"""
+
+    # Expected resources were obtained manually based on
+    # https://arxiv.org/abs/2506.15784
+    hamiltonian_data = [
+        (
+            8,
+            4,
+            100,
+            2,
+            {
+                "qubit_manager": QubitManager(
+                    work_wires={"clean": 0, "dirty": 0}, algo_wires=16, tight_budget=False
+                ),
+                "gate_types": defaultdict(
+                    int,
+                    {
+                        "T": 7711424.0,
+                        "S": 201936.0,
+                        "Z": 134624.0,
+                        "Hadamard": 134624.0,
+                        "CNOT": 187312.0,
+                    },
+                ),
+            },
+        ),
+        (
+            10,
+            5,
+            1000,
+            1,
+            {
+                "qubit_manager": QubitManager(
+                    work_wires={"clean": 0, "dirty": 0}, algo_wires=20, tight_budget=False
+                ),
+                "gate_types": defaultdict(
+                    int,
+                    {
+                        "T": 99920000.0,
+                        "S": 2700000.0,
+                        "Z": 1800000.0,
+                        "Hadamard": 1800000.0,
+                        "CNOT": 2420000.0,
+                    },
+                ),
+            },
+        ),
+        (
+            12,
+            8,
+            750,
+            4,
+            {
+                "qubit_manager": QubitManager(
+                    work_wires={"clean": 0, "dirty": 0}, algo_wires=24, tight_budget=False
+                ),
+                "gate_types": defaultdict(
+                    int,
+                    {
+                        "T": 1593920064.0,
+                        "S": 41580792.0,
+                        "Z": 27720528.0,
+                        "Hadamard": 27720528.0,
+                        "CNOT": 40770264.0,
+                    },
+                ),
+            },
+        ),
+    ]
+
+    @pytest.mark.parametrize(
+        "num_orbitals, num_fragments, num_steps, order, expected_res", hamiltonian_data
+    )
+    def test_resource_trotter_cdf(
+        self, num_orbitals, num_fragments, num_steps, order, expected_res
+    ):
+        """Test the ResourceTrotterCDF class for correct resources"""
+
+        compact_ham = plre.CompactHamiltonian.cdf(
+            num_orbitals=num_orbitals, num_fragments=num_fragments
+        )
+
+        def circ():
+            plre.ResourceTrotterCDF(compact_ham, num_steps=num_steps, order=order)
+
+        res = plre.estimate_resources(circ)()
+        print(res, expected_res)
+        assert res.qubit_manager == expected_res["qubit_manager"]
+        assert res.clean_gate_counts == expected_res["gate_types"]
+
+    def test_type_error(self):
+        r"""Test that a TypeError is raised for unsupported Hamiltonian representations."""
+        compact_ham = plre.CompactHamiltonian.thc(num_orbitals=4, tensor_rank=10)
+        with pytest.raises(
+            TypeError, match="Unsupported Hamiltonian representation for ResourceTrotterCDF"
+        ):
+            plre.ResourceTrotterCDF(compact_ham, num_steps=100, order=2)
+
+
+class TestTrotterTHC:
+    """Tests for ResourceTrotterCDF class"""
+
+    # Expected resources were obtained manually
+    # based on https://arxiv.org/abs/2407.04432
+
+    hamiltonian_data = [
+        (
+            8,
+            20,
+            100,
+            2,
+            {
+                "qubit_manager": QubitManager(
+                    work_wires={"clean": 0, "dirty": 0}, algo_wires=40, tight_budget=False
+                ),
+                "gate_types": defaultdict(
+                    int,
+                    {
+                        "T": 9687424.0,
+                        "S": 261936.0,
+                        "Z": 174624.0,
+                        "Hadamard": 174624.0,
+                        "CNOT": 243312.0,
+                    },
+                ),
+            },
+        ),
+        (
+            10,
+            40,
+            1000,
+            1,
+            {
+                "qubit_manager": QubitManager(
+                    work_wires={"clean": 0, "dirty": 0}, algo_wires=80, tight_budget=False
+                ),
+                "gate_types": defaultdict(
+                    int,
+                    {
+                        "T": 368720000.0,
+                        "S": 9900000.0,
+                        "Z": 6600000.0,
+                        "Hadamard": 6600000.0,
+                        "CNOT": 9620000.0,
+                    },
+                ),
+            },
+        ),
+    ]
+
+    @pytest.mark.parametrize(
+        "num_orbitals, tensor_rank, num_steps, order, expected_res", hamiltonian_data
+    )
+    def test_resource_trotter_thc(self, num_orbitals, tensor_rank, num_steps, order, expected_res):
+        """Test the ResourceTrotterTHC class for correct resources"""
+        compact_ham = plre.CompactHamiltonian.thc(
+            num_orbitals=num_orbitals, tensor_rank=tensor_rank
+        )
+
+        def circ():
+            plre.ResourceTrotterTHC(compact_ham, num_steps=num_steps, order=order)
+
+        res = plre.estimate_resources(circ)()
+
+        assert res.qubit_manager == expected_res["qubit_manager"]
+        assert res.clean_gate_counts == expected_res["gate_types"]
+
+    def test_type_error(self):
+        """Test that a TypeError is raised for unsupported Hamiltonian representations."""
+        compact_ham = plre.CompactHamiltonian.cdf(num_orbitals=4, num_fragments=10)
+        with pytest.raises(
+            TypeError, match="Unsupported Hamiltonian representation for ResourceTrotterTHC"
+        ):
+            plre.ResourceTrotterTHC(compact_ham, num_steps=100, order=2)