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

# 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 state preparation templates."""
import math
from typing import Dict

import pennylane.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=arguments-differ, protected-access, non-parent-init-called, too-many-arguments, unused-argument


[docs] class ResourceUniformStatePrep(ResourceOperator): r"""Resource class for preparing a uniform superposition. This operation prepares a uniform superposition over a given number of basis states. The uniform superposition is defined as: .. math:: \frac{1}{\sqrt{l}} \sum_{i=0}^{l} |i\rangle where :math:`l` is the number of states. This operation uses ``Hadamard`` gates to create the uniform superposition when the number of states is a power of two. If the number of states is not a power of two, amplitude amplification technique defined in `arXiv:1805.03662 <https://arxiv.org/pdf/1805.03662>`_ is used. Args: num_states (int): the number of states in the uniform superposition wires (Sequence[int], optional): the wires the operation acts on Resources: The resources are obtained from Figure 12 in `arXiv:1805.03662 <https://arxiv.org/pdf/1805.03662>`_. The circuit uses amplitude amplification to prepare a uniform superposition over :math:`l` basis states. **Example** The resources for this operation are computed using: >>> unif_state_prep = plre.ResourceUniformStatePrep(10) >>> print(plre.estimate_resources(unif_state_prep)) --- Resources: --- Total qubits: 5 Total gates : 124 Qubit breakdown: clean qubits: 1, dirty qubits: 0, algorithmic qubits: 4 Gate breakdown: {'Hadamard': 16, 'X': 12, 'CNOT': 4, 'Toffoli': 4, 'T': 88} """ resource_keys = {"num_states"} def __init__(self, num_states, wires=None): self.num_states = num_states k = (num_states & -num_states).bit_length() - 1 L = num_states // (2**k) if L == 1: self.num_wires = k else: self.num_wires = k + int(math.ceil(math.log2(L))) super().__init__(wires=wires) @property def resource_params(self): r"""Returns a dictionary containing the minimal information needed to compute the resources. Returns: dict: A dictionary containing the resource parameters: * num_states (int): the number of states over which the uniform superposition is being prepared """ return {"num_states": self.num_states}
[docs] @classmethod def resource_rep(cls, num_states: int) -> CompressedResourceOp: r"""Returns a compressed representation containing only the parameters of the Operator that are needed to compute the resources. Returns: CompressedResourceOp: the operator in a compressed representation """ return CompressedResourceOp(cls, {"num_states": num_states})
[docs] @classmethod def default_resource_decomp(cls, num_states, **kwargs): r"""Returns a list representing the resources of the operator. Each object in the list represents a gate and the number of times it occurs in the circuit. Args: num_states (int): the number of states over which the uniform superposition is being prepared Resources: The resources are obtained from Figure 12 in `arXiv:1805.03662 <https://arxiv.org/pdf/1805.03662>`_. The circuit uses amplitude amplification to prepare a uniform superposition over :math:`l` basis states. 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_lst = [] k = (num_states & -num_states).bit_length() - 1 L = num_states // (2**k) if L == 1: gate_lst.append(GateCount(resource_rep(plre.ResourceHadamard), k)) return gate_lst logl = int(math.ceil(math.log2(L))) gate_lst.append(GateCount(resource_rep(plre.ResourceHadamard), k + 3 * logl)) gate_lst.append( GateCount( resource_rep(plre.ResourceIntegerComparator, {"value": L, "register_size": logl}), 1 ) ) gate_lst.append(GateCount(resource_rep(plre.ResourceRZ), 2)) gate_lst.append( GateCount( resource_rep( plre.ResourceAdjoint, { "base_cmpr_op": resource_rep( plre.ResourceIntegerComparator, {"value": L, "register_size": logl} ) }, ), 1, ) ) return gate_lst
[docs] class ResourceAliasSampling(ResourceOperator): r"""Resource class for preparing a state using coherent alias sampling. Args: num_coeffs (int): the number of unique coefficients in the state precision (float): the precision with which the coefficients are loaded wires (Sequence[int], optional): the wires the operation acts on Resources: The resources are obtained from Section III D in `arXiv:1805.03662 <https://arxiv.org/pdf/1805.03662>`_. The circuit uses coherent alias sampling to prepare a state with the given coefficients. **Example** The resources for this operation are computed using: >>> alias_sampling = plre.ResourceAliasSampling(num_coeffs=100) >>> print(plre.estimate_resources(alias_sampling)) --- Resources: --- Total qubits: 81 Total gates : 6.157E+3 Qubit breakdown: clean qubits: 6, dirty qubits: 68, algorithmic qubits: 7 Gate breakdown: {'Hadamard': 730, 'X': 479, 'CNOT': 4.530E+3, 'Toffoli': 330, 'T': 88} """ resource_keys = {"num_coeffs", "precision"} def __init__(self, num_coeffs, precision=None, wires=None): self.num_coeffs = num_coeffs self.precision = precision self.num_wires = int(math.ceil(math.log2(num_coeffs))) super().__init__(wires=wires) @property def resource_params(self): r"""Returns a dictionary containing the minimal information needed to compute the resources. Returns: dict: A dictionary containing the resource parameters: * num_coeffs (int): the number of unique coefficients in the state * precision (float): the precision with which the coefficients are loaded """ return {"num_coeffs": self.num_coeffs, "precision": self.precision}
[docs] @classmethod def resource_rep(cls, num_coeffs, precision=None) -> CompressedResourceOp: r"""Returns a compressed representation containing only the parameters of the Operator that are needed to compute the resources. Returns: CompressedResourceOp: the operator in a compressed representation """ return CompressedResourceOp(cls, {"num_coeffs": num_coeffs, "precision": precision})
[docs] @classmethod def default_resource_decomp(cls, num_coeffs, precision=None, **kwargs) -> list[GateCount]: r"""Returns a list representing the resources of the operator. Each object in the list represents a gate and the number of times it occurs in the circuit. Args: num_coeffs (int): the number of unique coefficients in the state precision (float): the precision with which the coefficients are loaded Resources: The resources are obtained from Section III D in `arXiv:1805.03662 <https://arxiv.org/pdf/1805.03662>`_. The circuit uses coherent alias sampling to prepare a state with the given coefficients. 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_lst = [] logl = int(math.ceil(math.log2(num_coeffs))) precision = precision or kwargs["config"]["precision_alias_sampling"] num_prec_wires = abs(math.floor(math.log2(precision))) gate_lst.append(AllocWires(logl + 2 * num_prec_wires + 1)) gate_lst.append( GateCount(resource_rep(plre.ResourceUniformStatePrep, {"num_states": num_coeffs}), 1) ) gate_lst.append(GateCount(resource_rep(plre.ResourceHadamard), num_prec_wires)) gate_lst.append( GateCount( resource_rep( plre.ResourceQROM, {"num_bitstrings": num_coeffs, "size_bitstring": logl + num_prec_wires}, ), 1, ) ) gate_lst.append( GateCount( resource_rep( plre.ResourceRegisterComparator, {"first_register": num_prec_wires, "second_register": num_prec_wires}, ), 1, ) ) gate_lst.append(GateCount(resource_rep(plre.ResourceCSWAP), logl)) return gate_lst
[docs] class ResourceMPSPrep(ResourceOperator): r"""Resource class for the MPSPrep template. The resource operation for preparing an initial state from a matrix product state (MPS) representation. Args: num_mps_matrices (int): the number of matrices in the MPS representation max_bond_dim (int): the bond dimension of the MPS representation precision (Union[None, float], optional): the precision used when loading the MPS matricies wires (Sequence[int], optional): the wires the operation acts on Resources: The resources for MPSPrep are according to the decomposition, which uses the generic :class:`~.labs.resource_estimation.ResourceQubitUnitary`. The decomposition is based on the routine described in `arXiv:2310.18410 <https://arxiv.org/pdf/2310.18410>`_. .. seealso:: :class:`~.MPSPrep` **Example** The resources for this operation are computed using: >>> mps = plre.ResourceMPSPrep(num_mps_matrices=10, max_bond_dim=2**3) >>> print(plre.estimate_resources(mps, gate_set={"CNOT", "RZ", "RY"})) --- Resources: --- Total qubits: 13 Total gates : 1.654E+3 Qubit breakdown: clean qubits: 3, dirty qubits: 0, algorithmic qubits: 10 Gate breakdown: {'RZ': 728, 'CNOT': 774, 'RY': 152} """ resource_keys = {"num_mps_matrices", "max_bond_dim", "precision"} def __init__(self, num_mps_matrices, max_bond_dim, precision=None, wires=None): self.num_wires = num_mps_matrices self.precision = precision self.max_bond_dim = max_bond_dim self.num_mps_matrices = num_mps_matrices 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_mps_matrices (int): the number of matrices in the MPS representation * max_bond_dim (int): the bond dimension of the MPS representation * precision (Union[None, float], optional): the precision used when loading the MPS matrices """ return { "num_mps_matrices": self.num_mps_matrices, "max_bond_dim": self.max_bond_dim, "precision": self.precision, }
[docs] @classmethod def resource_rep(cls, num_mps_matrices, max_bond_dim, precision=None) -> CompressedResourceOp: r"""Returns a compressed representation containing only the parameters of the Operator that are needed to compute the resources. Args: num_mps_matrices (int): the number of matrices in the MPS representation max_bond_dim (int): the bond dimension of the MPS representation precision (Union[None, float], optional): the precision used when loading the MPS matrices Returns: CompressedResourceOp: the operator in a compressed representation """ params = { "num_mps_matrices": num_mps_matrices, "max_bond_dim": max_bond_dim, "precision": precision, } return CompressedResourceOp(cls, params)
[docs] @classmethod def default_resource_decomp( cls, num_mps_matrices, max_bond_dim, precision=None, **kwargs, ) -> list[GateCount]: r"""Returns a list representing the resources of the operator. Each object in the list represents a gate and the number of times it occurs in the circuit. Args: num_mps_matrices (int): the number of matrices in the MPS representation max_bond_dim (int): the bond dimension of the MPS representation precision (Union[None, float], optional): the precision used when loading the MPS matrices Resources: The resources for MPSPrep are estimated according to the decomposition, which uses the generic :class:`~.labs.resource_estimation.ResourceQubitUnitary`. The decomposition is based on the routine described in `arXiv:2310.18410 <https://arxiv.org/pdf/2310.18410>`_. 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. """ precision = precision or kwargs["config"]["precision_mps_prep"] num_work_wires = min( math.ceil(math.log2(max_bond_dim)), math.ceil(num_mps_matrices / 2) # truncate bond dim ) gate_lst = [AllocWires(num_work_wires)] for index in range(1, num_mps_matrices + 1): qubit_unitary_wires = min(index + 1, num_work_wires + 1, (num_mps_matrices - index) + 2) qubit_unitary = plre.ResourceQubitUnitary.resource_rep( num_wires=qubit_unitary_wires, precision=precision ) gate_lst.append(GateCount(qubit_unitary)) gate_lst.append(FreeWires(num_work_wires)) return gate_lst
[docs] @classmethod def tracking_name(cls, num_mps_matrices, max_bond_dim, precision) -> str: return f"MPSPrep({num_mps_matrices}, {max_bond_dim}, {precision})"
[docs] class ResourceQROMStatePreparation(ResourceOperator): r"""Resource class for the QROMStatePreparation template. This operation implements the state preparation method described in `arXiv:0208112 <https://arxiv.org/abs/quant-ph/0208112>`_, using :class:`~.labs.resource_estimation.ResourceQROM` to dynamically load the rotation angles. .. note:: This decomposition assumes an appropriately sized phase gradient state is available. Users should ensure the cost of constructing such a state has been accounted for. See also :class:`~.pennylane.labs.resource_estimation.ResourcePhaseGradient`. Args: num_state_qubits (int): number of qubits required to represent the state-vector precision (float): the precision threshold for loading in the binary representation of the rotation angles positive_and_real (bool): flag that the coefficients of the statevector are all real and positive select_swap_depths (Union[None, int, Iterable(int)], optional): a parameter of :code:`QROM` used to trade-off extra qubits for reduced circuit depth wires (Sequence[int], optional): the wires the operation acts on Resources: The resources for QROMStatePreparation are computed according to the decomposition described in `arXiv:0208112 <https://arxiv.org/abs/quant-ph/0208112>`_, using :class:`~.labs.resource_estimation.ResourceQROM` to dynamically load the rotation angles. These rotations gates are implemented using an in-place controlled-adder operation (see figure 4. of `arXiv:2409.07332 <https://arxiv.org/pdf/2409.07332>`_) to a phase gradient. .. seealso:: :class:`~.QROMStatePreparation` **Example** The resources for this operation are computed using: >>> qrom_prep = plre.ResourceQROMStatePreparation(num_state_qubits=5, precision=1e-3) >>> print(plre.estimate_resources(qrom_prep, gate_set=plre.StandardGateSet)) --- Resources: --- Total qubits: 28 Total gates : 2.744E+3 Qubit breakdown: clean qubits: 23, dirty qubits: 0, algorithmic qubits: 5 Gate breakdown: {'X': 230, 'Toffoli': 236, 'CNOT': 1.522E+3, 'Hadamard': 732, 'S': 12, 'Adjoint(S)': 12} .. details:: :title: Usage Details This operation uses the :code:`QROM` subroutine to dynamically load the rotation angles. >>> gate_set = {"QROM", "Hadamard", "CNOT", "T", "Adjoint(QROM)"} >>> qrom_prep = plre.ResourceQROMStatePreparation( ... num_state_qubits = 4, ... precision = 1e-2, ... select_swap_depths = 2, # default value is 1 ... ) >>> res = plre.estimate_resources(qrom_prep, gate_set) >>> print(res) --- Resources: --- Total qubits: 21 Total gates : 2.680E+3 Qubit breakdown: clean qubits: 17, dirty qubits: 0, algorithmic qubits: 4 Gate breakdown: {'QROM': 5, 'Adjoint(QROM)': 5, 'CNOT': 580, 'T': 1.832E+3, 'Hadamard': 258} The ``precision`` argument is used to allocate the target wires in the underlying QROM operations. It corresponds to the precision with which the rotation angles of the template are encoded. This means that the binary representation of the angle is truncated up to the :math:`m`-th digit, where :math:`m` is the number of precision wires allocated. See Eq. 5 in `arXiv:0208112 <https://arxiv.org/abs/quant-ph/0208112>`_ for more details. The ``select_swap_depths`` parameter allows a user to configure the ``select_swap_depth`` of each individual :class:`~.labs.resource_estimation.ResourceQROM` used. The ``select_swap_depths`` argument can be one of :code:`(int, None, Iterable(int, None))`. If an integer or :code:`None` is passed (the default value for this parameter is 1), then that is used as the ``select_swap_depth`` for all :code:`QROM` operations in the resource decomposition. >>> for op in res.gate_types: ... if op.name == "QROM": ... print(op.name, op.params) ... QROM {'num_bitstrings': 1, 'num_bit_flips': 4, 'size_bitstring': 9, 'select_swap_depth': 2, 'clean': False} QROM {'num_bitstrings': 2, 'num_bit_flips': 9, 'size_bitstring': 9, 'select_swap_depth': 2, 'clean': False} QROM {'num_bitstrings': 4, 'num_bit_flips': 18, 'size_bitstring': 9, 'select_swap_depth': 2, 'clean': False} QROM {'num_bitstrings': 8, 'num_bit_flips': 36, 'size_bitstring': 9, 'select_swap_depth': 2, 'clean': False} QROM {'num_bitstrings': 16, 'num_bit_flips': 72, 'size_bitstring': 9, 'select_swap_depth': 2, 'clean': False} Alternatively, we can configure each value independently by specifying a list. Note the size of this list should be :code:`num_state_qubits + 1` (:code:`num_state_qubits` if the state is positive and real). >>> qrom_prep = plre.ResourceQROMStatePreparation( ... num_state_qubits = 4, ... precision = 1e-2, ... select_swap_depths = [1, None, 2, 2, None], ... ) >>> res = plre.estimate_resources(qrom_prep, gate_set) >>> for op in res.gate_types: ... if op.name == "QROM": ... print(op.name, op.params) ... QROM {'num_bitstrings': 1, 'num_bit_flips': 4, 'size_bitstring': 9, 'select_swap_depth': 1, 'clean': False} QROM {'num_bitstrings': 2, 'num_bit_flips': 9, 'size_bitstring': 9, 'select_swap_depth': None, 'clean': False} QROM {'num_bitstrings': 4, 'num_bit_flips': 18, 'size_bitstring': 9, 'select_swap_depth': 2, 'clean': False} QROM {'num_bitstrings': 8, 'num_bit_flips': 36, 'size_bitstring': 9, 'select_swap_depth': 2, 'clean': False} QROM {'num_bitstrings': 16, 'num_bit_flips': 72, 'size_bitstring': 9, 'select_swap_depth': None, 'clean': False} """ resource_keys = {"num_state_qubits", "precision", "positive_and_real", "selswap_depths"} def __init__( self, num_state_qubits, precision=None, positive_and_real=False, select_swap_depths=1, wires=None, ): # Overriding the default init method to allow for CompactState as an input. self.num_wires = num_state_qubits self.precision = precision self.positive_and_real = positive_and_real expected_size = num_state_qubits if positive_and_real else num_state_qubits + 1 if isinstance(select_swap_depths, (list, tuple, np.ndarray)): if len(select_swap_depths) != expected_size: raise ValueError( f"Expected the length of `select_swap_depths` to be {expected_size}, got {len(select_swap_depths)}" ) elif not (isinstance(select_swap_depths, int) or select_swap_depths is None): raise TypeError("`select_swap_depths` must be an integer, None or iterable") self.selswap_depths = select_swap_depths 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_state_qubits (int): number of qubits required to represent the state-vector * precision (float): the precision threshold for loading in the binary representation of the rotation angles * positive_and_real (bool): flag that the coefficients of the statevector are all real and positive * selswap_depths (Union[None, int, Iterable(int)], optional): a parameter of :code:`QROM` used to trade-off extra qubits for reduced circuit depth """ return { "num_state_qubits": self.num_wires, "precision": self.precision, "positive_and_real": self.positive_and_real, "selswap_depths": self.selswap_depths, }
[docs] @classmethod def resource_rep( cls, num_state_qubits, precision=None, positive_and_real=False, selswap_depths=1 ): r"""Returns a compressed representation containing only the parameters of the Operator that are needed to compute the resources. Args: num_state_qubits (int): number of qubits required to represent the state-vector precision (float): the precision threshold for loading in the binary representation of the rotation angles positive_and_real (bool): flag that the coefficients of the statevector are all real and positive selswap_depths (Union[None, int, Iterable(int)], optional): a parameter of :code:`QROM` used to trade-off extra qubits for reduced circuit depth Returns: CompressedResourceOp: the operator in a compressed representation """ expected_size = num_state_qubits if positive_and_real else num_state_qubits + 1 if isinstance(selswap_depths, (list, tuple, np.ndarray)): if len(selswap_depths) != expected_size: raise ValueError( f"Expected the length of `selswap_depths` to be {expected_size}, got {len(selswap_depths)}" ) elif not (isinstance(selswap_depths, int) or selswap_depths is None): raise TypeError("`selswap_depths` must be an integer, None or iterable") params = { "num_state_qubits": num_state_qubits, "precision": precision, "positive_and_real": positive_and_real, "selswap_depths": selswap_depths, } return CompressedResourceOp(cls, params)
@classmethod def _decomp_selection_helper( cls, use_phase_grad_trick, num_state_qubits, positive_and_real, precision=None, selswap_depths=1, **kwargs, ): r"""A private function which implements two variants of the decomposition of QROMStatePrep, based on the value of the :code:`use_phase_grad_trick` argument. Returns a list representing the resources of the operator. Each object in the list represents a gate and the number of times it occurs in the circuit. Args: use_phase_grad_trick (bool): a flag which determines if the phase gradient trick is used instead of controlled-RY gates and phaseshifts. num_state_qubits (int): number of qubits required to represent the state-vector positive_and_real (bool): flag that the coefficients of the statevector are all real and positive precision (float): the precision threshold for loading in the binary representation of the rotation angles select_swap_depths (Union[None, int, Iterable(int)], optional): a parameter of :code:`QROM` used to trade-off extra qubits for reduced circuit depth Resources: The resources for QROMStatePreparation are according to the decomposition as described in `arXiv:0208112 <https://arxiv.org/abs/quant-ph/0208112>`_, using :class:`~.labs.resource_estimation.ResourceQROM` to dynamically load the rotation angles. Controlled-RY (and phase shifts) gates are used to apply all of the rotations coherently. If :code:`use_phase_grad_trick == True` then these rotations gates are implmented using an inplace controlled semi-adder operation (see figure 4. of `arXiv:2409.07332 <https://arxiv.org/pdf/2409.07332>`_). 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_counts = [] precision = precision or kwargs["config"]["precision_qrom_state_prep"] expected_size = num_state_qubits if positive_and_real else num_state_qubits + 1 if isinstance(selswap_depths, int) or selswap_depths is None: selswap_depths = [selswap_depths] * expected_size num_precision_wires = math.ceil(math.log2(math.pi / precision)) gate_counts.append(AllocWires(num_precision_wires)) for j in range(num_state_qubits): num_bitstrings = 2**j num_bit_flips = num_bitstrings * num_precision_wires // 2 gate_counts.append( GateCount( plre.ResourceQROM.resource_rep( num_bitstrings=num_bitstrings, size_bitstring=num_precision_wires, num_bit_flips=num_bit_flips, clean=False, select_swap_depth=selswap_depths[j], ) ) ) gate_counts.append( GateCount( plre.ResourceAdjoint.resource_rep( plre.resource_rep( plre.ResourceQROM, { "num_bitstrings": num_bitstrings, "num_bit_flips": num_bit_flips, "size_bitstring": num_precision_wires, "clean": False, "select_swap_depth": selswap_depths[j], }, ), ) ) ) if use_phase_grad_trick: semi_adder = plre.ResourceSemiAdder.resource_rep(max_register_size=num_precision_wires) h = plre.ResourceHadamard.resource_rep() s = plre.ResourceS.resource_rep() s_dagg = plre.ResourceAdjoint.resource_rep(base_cmpr_op=s) gate_counts.append( GateCount( plre.ResourceControlled.resource_rep( base_cmpr_op=semi_adder, num_ctrl_wires=1, num_ctrl_values=0 ), count=num_state_qubits, ) ) gate_counts.append(GateCount(h, 2 * num_precision_wires)) gate_counts.append(GateCount(s, num_precision_wires)) gate_counts.append( GateCount(s_dagg, num_precision_wires) ) # map RY rotations to RZ for phase grad else: cry = plre.ResourceCRY.resource_rep() gate_counts.append(GateCount(cry, num_precision_wires * num_state_qubits)) if not positive_and_real: gate_counts.append( GateCount( plre.ResourceQROM.resource_rep( num_bitstrings=2**num_state_qubits, size_bitstring=num_precision_wires, num_bit_flips=((2**num_state_qubits) * num_precision_wires // 2), clean=False, select_swap_depth=selswap_depths[-1], ) ) ) gate_counts.append( GateCount( plre.ResourceAdjoint.resource_rep( plre.resource_rep( plre.ResourceQROM, { "num_bitstrings": 2**num_state_qubits, "size_bitstring": num_precision_wires, "num_bit_flips": ((2**num_state_qubits) * num_precision_wires // 2), "clean": False, "select_swap_depth": selswap_depths[-1], }, ), ) ) ) if use_phase_grad_trick: semi_adder = plre.ResourceSemiAdder.resource_rep( max_register_size=num_precision_wires ) gate_counts.append( GateCount( plre.ResourceControlled.resource_rep( base_cmpr_op=semi_adder, num_ctrl_wires=1, num_ctrl_values=0 ), ) ) else: phase_shift = plre.ResourcePhaseShift.resource_rep() gate_counts.append(GateCount(phase_shift, num_precision_wires)) gate_counts.append(FreeWires(num_precision_wires)) return gate_counts
[docs] @classmethod def controlled_ry_resource_decomp( cls, num_state_qubits, positive_and_real, precision=None, selswap_depths=1, **kwargs, ): r"""Returns a list representing the resources of the operator. Each object in the list represents a gate and the number of times it occurs in the circuit. Args: num_state_qubits (int): number of qubits required to represent the state-vector positive_and_real (bool): Flag that the coefficients of the statevector are all real and positive. precision (float): The precision threshold for loading in the binary representation of the rotation angles. select_swap_depths (Union[None, int, Iterable(int)], optional): A parameter of :code:`QROM` used to trade-off extra qubits for reduced circuit depth. Resources: The resources for QROMStatePreparation are according to the decomposition as described in `arXiv:0208112 <https://arxiv.org/abs/quant-ph/0208112>`_, using :class:`~.labs.resource_estimation.ResourceQROM` to dynamically load the rotation angles. Controlled-RY (and phase shifts) gates are used to apply all of the rotations coherently. 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 cls._decomp_selection_helper( use_phase_grad_trick=False, num_state_qubits=num_state_qubits, positive_and_real=positive_and_real, precision=precision, selswap_depths=selswap_depths, **kwargs, )
[docs] @classmethod def default_resource_decomp( cls, num_state_qubits, positive_and_real, precision=None, selswap_depths=1, **kwargs ): r"""Returns a list representing the resources of the operator. Each object in the list represents a gate and the number of times it occurs in the circuit. .. note:: This decomposition assumes an appropriately sized phase gradient state is available. Users should ensure the cost of constructing such a state has been accounted for. See also :class:`~.pennylane.labs.resource_estimation.ResourcePhaseGradient`. Args: num_state_qubits (int): number of qubits required to represent the state-vector positive_and_real (bool): Flag that the coefficients of the statevector are all real and positive. precision (float): The precision threshold for loading in the binary representation of the rotation angles. select_swap_depths (Union[None, int, Iterable(int)], optional): A parameter of :code:`QROM` used to trade-off extra qubits for reduced circuit depth. Resources: The resources for QROMStatePreparation are according to the decomposition as described in `arXiv:0208112 <https://arxiv.org/abs/quant-ph/0208112>`_, using :class:`~.labs.resource_estimation.ResourceQROM` to dynamically load the rotation angles. These rotations gates are implmented using an inplace controlled-adder operation (see figure 4. of `arXiv:2409.07332 <https://arxiv.org/pdf/2409.07332>`_) to phase gradient. 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 cls._decomp_selection_helper( use_phase_grad_trick=True, num_state_qubits=num_state_qubits, positive_and_real=positive_and_real, precision=precision, selswap_depths=selswap_depths, **kwargs, )