Source code for pennylane.labs.resource_estimation.templates.qubitize

# 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 PennyLane subroutine templates."""
import math

import numpy as np

from pennylane.labs import resource_estimation as plre
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=too-many-arguments, arguments-differ


[docs] class ResourceQubitizeTHC(ResourceOperator): r"""Resource class for qubitization of tensor hypercontracted Hamiltonian. .. note:: This decomposition assumes that an appropriately sized phase gradient state is available. Users should ensure that the cost of constructing this state has been accounted for. See also :class:`~.pennylane.labs.resource_estimation.ResourcePhaseGradient`. Args: compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted Hamiltonian for which the walk operator is being created prep_op (Union[~pennylane.labs.resource_estimation.ResourceOperator, None]): An optional resource operator, corresponding to the prepare routine. If :code:`None`, the default :class:`~.pennylane.labs.resource_estimation.ResourcePrepTHC` will be used. select_op (Union[~pennylane.labs.resource_estimation.ResourceOperator, None]): An optional resource operator, corresponding to the select routine. If :code:`None`, the default :class:`~.pennylane.labs.resource_estimation.ResourceSelectTHC` will be used. wires (list[int] or optional): the wires on which the operator acts Resources: The resources are calculated based on `arXiv:2011.03494 <https://arxiv.org/abs/2011.03494>`_ **Example** The resources for this operation are computed using: >>> compact_ham = plre.CompactHamiltonian.thc(num_orbitals=20, tensor_rank=40) >>> prep = plre.ResourcePrepTHC(compact_ham, coeff_precision=20, select_swap_depth=2) >>> res = plre.estimate(plre.ResourceQubitizeTHC(compact_ham, prep_op=prep)) >>> print(res) --- Resources: --- Total qubits: 381 Total gates : 5.628E+4 Qubit breakdown: clean qubits: 313, dirty qubits: 0, algorithmic qubits: 68 Gate breakdown: {'Toffoli': 3.504E+3, 'CNOT': 4.138E+4, 'X': 2.071E+3, 'Hadamard': 9.213E+3, 'S': 80, 'Z': 41} """ resource_keys = {"compact_ham", "prep_op", "select_op"} def __init__( self, compact_ham, prep_op=None, select_op=None, wires=None, ): if compact_ham.method_name != "thc": raise TypeError( f"Unsupported Hamiltonian representation for ResourceQubitizeTHC." f"This method works with thc Hamiltonian, {compact_ham.method_name} provided" ) self.compact_ham = compact_ham self.prep_op = prep_op.resource_rep_from_op() if prep_op else None self.select_op = select_op.resource_rep_from_op() if select_op else None num_orb = compact_ham.params["num_orbitals"] tensor_rank = compact_ham.params["tensor_rank"] num_coeff = num_orb + tensor_rank * (tensor_rank + 1) / 2 # N+M(M+1)/2 coeff_register = int(math.ceil(math.log2(num_coeff))) # Based on section III D, Eq. 43 in arXiv:2011.03494 # Numbers have been adjusted to remove the auxilliary qubits accounted for by different templates self.num_wires = ( num_orb * 2 + 2 * int(np.ceil(math.log2(tensor_rank + 1))) + coeff_register + 6 ) 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 (CompactHamiltonian): a tensor hypercontracted Hamiltonian for which the walk operator is being created * prep_op (Union[CompressedResourceOp, None]): An optional compressed resource operator, corresponding to the prepare routine. If :code:`None`, the default :class:`~.pennylane.labs.resource_estimation.ResourcePrepTHC` will be used. * select_op (Union[CompressedResourceOp, None]): An optional compressed resource operator, corresponding to the select routine. If :code:`None`, the default :class:`~.pennylane.labs.resource_estimation.ResourceSelectTHC` will be used. """ return { "compact_ham": self.compact_ham, "prep_op": self.prep_op, "select_op": self.select_op, }
[docs] @classmethod def resource_rep(cls, compact_ham, prep_op=None, select_op=None) -> 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 tensor hypercontracted Hamiltonian for which the walk operator is being created prep_op (Union[~pennylane.labs.resource_estimation.CompressedResourceOp, None]): An optional compressed resource operator, corresponding to the prepare routine. If :code:`None`, the default :class:`~.pennylane.labs.resource_estimation.ResourcePrepTHC` will be used. select_op (Union[~pennylane.labs.resource_estimation.CompressedResourceOp, None]): An optional compressed resource operator, corresponding to the select routine. If :code:`None`, the default :class:`~.pennylane.labs.resource_estimation.ResourceSelectTHC` will be used. Returns: CompressedResourceOp: the operator in a compressed representation """ if compact_ham.method_name != "thc": raise TypeError( f"Unsupported Hamiltonian representation for ResourceQubitizeTHC." f"This method works with thc Hamiltonian, {compact_ham.method_name} provided" ) num_orb = compact_ham.params["num_orbitals"] tensor_rank = compact_ham.params["tensor_rank"] num_coeff = num_orb + tensor_rank * (tensor_rank + 1) / 2 # N+M(M+1)/2 coeff_register = int(math.ceil(math.log2(num_coeff))) # Based on section III D, Eq. 43 in arXiv:2011.03494 # Numbers have been adjusted to remove the auxilliary qubits accounted for by different templates num_wires = num_orb * 2 + 2 * int(np.ceil(math.log2(tensor_rank + 1))) + coeff_register + 6 params = {"compact_ham": compact_ham, "prep_op": prep_op, "select_op": select_op} return CompressedResourceOp(cls, num_wires, params)
[docs] @classmethod def resource_decomp( cls, compact_ham, prep_op=None, select_op=None, **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. .. note:: This decomposition assumes that an appropriately sized phase gradient state is available. Users should ensure that the cost of constructing this state has been accounted for. See also :class:`~.pennylane.labs.resource_estimation.ResourcePhaseGradient`. Args: compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted Hamiltonian for which the walk operator is being created prep_op (Union[~pennylane.labs.resource_estimation.CompressedResourceOp, None]): An optional compressed resource operator, corresponding to the prepare routine. If :code:`None`, the default :class:`~.pennylane.labs.resource_estimation.ResourcePrepTHC` will be used. select_op (Union[~pennylane.labs.resource_estimation.CompressedResourceOp, None]): An optional compressed resource operator, corresponding to the select routine. If :code:`None`, the default :class:`~.pennylane.labs.resource_estimation.ResourceSelectTHC` will be used. 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. """ gate_list = [] tensor_rank = compact_ham.params["tensor_rank"] m_register = int(np.ceil(np.log2(tensor_rank))) if not select_op: # Select cost from Figure 5 in arXiv:2011.03494 select_op = resource_rep( plre.ResourceSelectTHC, { "compact_ham": compact_ham, }, ) gate_list.append(GateCount(select_op)) if not prep_op: # Prep cost from Figure 3 and 4 in arXiv:2011.03494 prep_op = resource_rep( plre.ResourcePrepTHC, { "compact_ham": compact_ham, }, ) gate_list.append(GateCount(prep_op)) gate_list.append(GateCount(resource_rep(plre.ResourceAdjoint, {"base_cmpr_op": prep_op}))) # reflection cost from Eq. 44 in arXiv:2011.03494 coeff_precision = prep_op.params["coeff_precision"] or kwargs["coeff_precision"] toffoli = resource_rep(plre.ResourceToffoli) gate_list.append(GateCount(toffoli, 2 * m_register + coeff_precision + 4)) return gate_list
[docs] @classmethod def controlled_resource_decomp( cls, ctrl_num_ctrl_wires, ctrl_num_ctrl_values, compact_ham, prep_op=None, select_op=None, **kwargs, ) -> list[GateCount]: r"""Returns a list representing the resources for the controlled version of the operator. .. note:: This decomposition assumes that an appropriately sized phase gradient state is available. Users should ensure that the cost of constructing this state has been accounted for. See also :class:`~.pennylane.labs.resource_estimation.ResourcePhaseGradient`. 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 compact_ham (~pennylane.labs.resource_estimation.CompactHamiltonian): a tensor hypercontracted Hamiltonian for which the walk operator is being created prep_op (Union[~pennylane.labs.resource_estimation.CompressedResourceOp, None]): An optional compressed resource operator, corresponding to the prepare routine. If :code:`None`, the default :class:`~.pennylane.labs.resource_estimation.ResourcePrepTHC` will be used. select_op (Union[~pennylane.labs.resource_estimation.CompressedResourceOp, None]): An optional compressed resource operator, corresponding to the select routine. If :code:`None`, the default :class:`~.pennylane.labs.resource_estimation.ResourceSelectTHC` will be used. 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. """ gate_list = [] tensor_rank = compact_ham.params["tensor_rank"] m_register = int(np.ceil(np.log2(tensor_rank))) if ctrl_num_ctrl_wires > 1: mcx = resource_rep( plre.ResourceMultiControlledX, { "num_ctrl_wires": ctrl_num_ctrl_wires, "num_ctrl_values": ctrl_num_ctrl_values, }, ) gate_list.append(AllocWires(1)) gate_list.append(GateCount(mcx, 2)) if not select_op: # Controlled Select cost from Fig 5 in arXiv:2011.03494 select_op = resource_rep( plre.ResourceSelectTHC, { "compact_ham": compact_ham, }, ) gate_list.append( GateCount( resource_rep( plre.ResourceControlled, {"base_cmpr_op": select_op, "num_ctrl_wires": 1, "num_ctrl_values": 0}, ) ) ) if not prep_op: # Prep cost from Fig 3 and 4 in arXiv:2011.03494 prep_op = resource_rep( plre.ResourcePrepTHC, { "compact_ham": compact_ham, }, ) gate_list.append(GateCount(prep_op)) gate_list.append(GateCount(resource_rep(plre.ResourceAdjoint, {"base_cmpr_op": prep_op}))) # reflection cost from Eq. 44 in arXiv:2011.03494s coeff_precision = prep_op.params["coeff_precision"] or kwargs["coeff_precision"] toffoli = resource_rep(plre.ResourceToffoli) gate_list.append(GateCount(toffoli, 2 * m_register + coeff_precision + 4)) if ctrl_num_ctrl_wires > 1: gate_list.append(FreeWires(1)) elif ctrl_num_ctrl_values > 0: gate_list.append(GateCount(resource_rep(plre.ResourceX), 2 * ctrl_num_ctrl_values)) return gate_list