Source code for pennylane.labs.resource_estimation.ops.qubit.parametric_ops_multi_qubit

# 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 parametric multi qubit operations."""

import pennylane.labs.resource_estimation as re
from pennylane.labs.resource_estimation.resource_operator import (
    CompressedResourceOp,
    GateCount,
    ResourceOperator,
)

# pylint: disable=arguments-differ


[docs] class ResourceMultiRZ(ResourceOperator): r"""Resource class for the MultiRZ gate. Args: num_wires (int): the number of qubits the operation acts upon eps (float, optional): error threshold for Clifford+T decomposition of this operation wires (Sequence[int], optional): the wires the operation acts on Resources: The resources come from Section VIII (Figure 3) of `The Bravyi-Kitaev transformation for quantum computation of electronic structure <https://arxiv.org/pdf/1208.5986>`_ paper. Specifically, the resources are given by one :class:`~.ResourceRZ` gate and a cascade of :math:`2 * (n - 1)` :class:`~.ResourceCNOT` gates where :math:`n` is the number of qubits the gate acts on. .. seealso:: :class:`~.MultiRZ` **Example** The resources for this operation are computed using: >>> multi_rz = plre.ResourceMultiRZ(num_wires=3) >>> gate_set = {"CNOT", "RZ"} >>> >>> print(plre.estimate_resources(multi_rz, gate_set)) --- Resources: --- Total qubits: 3 Total gates : 5 Qubit breakdown: clean qubits: 0, dirty qubits: 0, algorithmic qubits: 3 Gate breakdown: {'CNOT': 4, 'RZ': 1} """ resource_keys = {"num_wires", "eps"} def __init__(self, num_wires, eps=None, wires=None) -> None: self.num_wires = num_wires self.eps = eps super().__init__(wires=wires)
[docs] @classmethod def default_resource_decomp(cls, num_wires, eps=None, **kwargs): r"""Returns a list representing the resources for a controlled version of the operator. Args: num_wires (int): the number of qubits the operation acts upon eps (float): error threshold for clifford plus T decomposition of this operation Resources: The resources come from Section VIII (Figure 3) of `The Bravyi-Kitaev transformation for quantum computation of electronic structure <https://arxiv.org/pdf/1208.5986>`_ paper. Specifically, the resources are given by one :class:`~.ResourceRZ` gate and a cascade of :math:`2 * (n - 1)` :class:`~.ResourceCNOT` gates where :math:`n` is the number of qubits the gate acts on. 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. """ cnot = re.ResourceCNOT.resource_rep() rz = re.ResourceRZ.resource_rep(eps=eps) return [GateCount(cnot, 2 * (num_wires - 1)), GateCount(rz)]
@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_wires (int): the number of qubits the operation acts upon * eps (float): error threshold for clifford plus T decomposition of this operation """ return {"num_wires": self.num_wires, "eps": self.eps}
[docs] @classmethod def resource_rep(cls, num_wires, eps=None): """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation. Args: num_wires (int): the number of qubits the operation acts upon eps (float): error threshold for clifford plus T decomposition of this operation Returns: CompressedResourceOp: the operator in a compressed representation """ return CompressedResourceOp(cls, {"num_wires": num_wires, "eps": eps})
[docs] @classmethod def default_adjoint_resource_decomp(cls, num_wires, eps=None) -> list[GateCount]: r"""Returns a list representing the resources for the adjoint of the operator. Args: num_wires (int): the number of qubits the operation acts upon eps (float): error threshold for clifford plus T decomposition of this operation Resources: The adjoint of this operator just changes the sign of the phase, thus 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_wires=num_wires, eps=eps))]
[docs] @classmethod def default_controlled_resource_decomp( cls, ctrl_num_ctrl_wires, ctrl_num_ctrl_values, num_wires, eps=None, ) -> 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 num_wires (int): the number of qubits the base operation acts upon eps (float): error threshold for clifford plus T decomposition of this operation Resources: The resources are derived from the following identity. If an operation :math:`\hat{A}` can be expressed as :math:`\hat{A} \ = \ \hat{U} \cdot \hat{B} \cdot \hat{U}^{\dagger}` then the controlled operation :math:`C\hat{A}` can be expressed as: .. math:: C\hat{A} \ = \ \hat{U} \cdot C\hat{B} \cdot \hat{U}^{\dagger} Specifically, the resources are one multi-controlled RZ-gate and a cascade of :math:`2 * (n - 1)` :class:`~.ResourceCNOT` gates where :math:`n` is the number of qubits the gate acts on. 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. """ cnot = re.resource_rep(re.ResourceCNOT) ctrl_rz = re.resource_rep( re.ResourceControlled, { "base_cmpr_op": re.resource_rep(re.ResourceRZ, {"eps": eps}), "num_ctrl_wires": ctrl_num_ctrl_wires, "num_ctrl_values": ctrl_num_ctrl_values, }, ) return [GateCount(cnot, 2 * (num_wires - 1)), GateCount(ctrl_rz)]
[docs] @classmethod def default_pow_resource_decomp(cls, pow_z, num_wires, 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 num_wires (int): the number of qubits the base operation acts upon eps (float): error threshold for clifford plus T decomposition of this operation Resources: Taking arbitrary powers of a general rotation produces a sum of rotations. The resources simplify to just one total multi-RZ 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(num_wires=num_wires, eps=eps))]
[docs] class ResourcePauliRot(ResourceOperator): r"""Resource class for the PauliRot gate. Args: pauli_string (str): a string describing the pauli operators that define the rotation eps (float, optional): error threshold for clifford plus T decomposition of this operation wires (Sequence[int], optional): the wire the operation acts on Resources: When the :code:`pauli_string` is a single Pauli operator (:code:`X, Y, Z, Identity`) the cost is the associated single qubit rotation (:code:`RX, RY, RZ, GlobalPhase`). The resources come from Section VIII (Figures 3 & 4) of `The Bravyi-Kitaev transformation for quantum computation of electronic structure <https://arxiv.org/pdf/1208.5986>`_ paper, in combination with the following identity: .. math:: \begin{align} \hat{X} &= \hat{H} \cdot \hat{Z} \cdot \hat{H}, \\ \hat{Y} &= \hat{S} \cdot \hat{H} \cdot \hat{Z} \cdot \hat{H} \cdot \hat{S}^{\dagger}. \end{align} Specifically, the resources are given by one :class:`~.ResourceRZ` gate and a cascade of :math:`2 * (n - 1)` :class:`~.ResourceCNOT` gates where :math:`n` is the number of qubits the gate acts on. Additionally, for each :code:`X` gate in the Pauli word we conjugate by a pair of :class:`~.ResourceHadamard` gates, and for each :code:`Y` gate in the Pauli word we conjugate by a pair of :class:`~.ResourceHadamard` and a pair of :class:`~.ResourceS` gates. .. seealso:: :class:`~.PauliRot` **Example** The resources for this operation are computed using: >>> pr = plre.ResourcePauliRot(pauli_string="XYZ") >>> print(plre.estimate_resources(pr, plre.StandardGate\ Set)) --- Resources: --- Total qubits: 3 Total gates : 11 Qubit breakdown: clean qubits: 0, dirty qubits: 0, algorithmic qubits: 3 Gate breakdown: {'Hadamard': 4, 'S': 1, 'Adjoint(S)': 1, 'RZ': 1, 'CNOT': 4} """ resource_keys = {"pauli_string", "eps"} def __init__(self, pauli_string, eps=None, wires=None) -> None: self.eps = eps self.pauli_string = pauli_string self.num_wires = len(pauli_string) super().__init__(wires=wires)
[docs] @classmethod def default_resource_decomp(cls, pauli_string, eps=None, **kwargs): r"""Returns a list of GateCount objects representing the operator's resources. Args: pauli_string (str): a string describing the pauli operators that define the rotation eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: When the :code:`pauli_string` is a single Pauli operator (:code:`X, Y, Z, Identity`) the cost is the associated single qubit rotation (:code:`RX, RY, RZ, GlobalPhase`). The resources come from Section VIII (Figures 3 & 4) of `The Bravyi-Kitaev transformation for quantum computation of electronic structure <https://arxiv.org/pdf/1208.5986>`_ paper, in combination with the following identity: .. math:: \begin{align} \hat{X} &= \hat{H} \cdot \hat{Z} \cdot \hat{H}, \\ \hat{Y} &= \hat{S} \cdot \hat{H} \cdot \hat{Z} \cdot \hat{H} \cdot \hat{S}^{\dagger}. \end{align} Specifically, the resources are given by one :class:`~.ResourceRZ` gate and a cascade of :math:`2 * (n - 1)` :class:`~.ResourceCNOT` gates where :math:`n` is the number of qubits the gate acts on. Additionally, for each :code:`X` gate in the Pauli word we conjugate by a pair of :class:`~.ResourceHadamard` gates, and for each :code:`Y` gate in the Pauli word we conjugate by a pair of :class:`~.ResourceHadamard` and a pair of :class:`~.ResourceS` gates. 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 (set(pauli_string) == {"I"}) or (len(pauli_string) == 0): gp = re.resource_rep(re.ResourceGlobalPhase) return [GateCount(gp)] if pauli_string == "X": return [GateCount(re.resource_rep(re.ResourceRX, {"eps": eps}))] if pauli_string == "Y": return [GateCount(re.resource_rep(re.ResourceRY, {"eps": eps}))] if pauli_string == "Z": return [GateCount(re.resource_rep(re.ResourceRZ, {"eps": eps}))] active_wires = len(pauli_string.replace("I", "")) h = re.resource_rep(re.ResourceHadamard) s = re.resource_rep(re.ResourceS) rz = re.resource_rep(re.ResourceRZ, {"eps": eps}) s_dagg = re.resource_rep( re.ResourceAdjoint, {"base_cmpr_op": re.resource_rep(re.ResourceS)}, ) cnot = re.resource_rep(re.ResourceCNOT) h_count = 0 s_count = 0 for gate in pauli_string: if gate == "X": h_count += 1 if gate == "Y": h_count += 1 s_count += 1 gate_types = [] if h_count: gate_types.append(GateCount(h, 2 * h_count)) if s_count: gate_types.append(GateCount(s, s_count)) gate_types.append(GateCount(s_dagg, s_count)) gate_types.append(GateCount(rz)) gate_types.append(GateCount(cnot, 2 * (active_wires - 1))) return gate_types
@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: * pauli_string (str): a string describing the pauli operators that define the rotation * eps (float): error threshold for clifford plus T decomposition of this operation """ return { "pauli_string": self.pauli_string, "eps": self.eps, }
[docs] @classmethod def resource_rep(cls, pauli_string, eps=None): """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation. Args: pauli_string (str): a string describing the pauli operators that define the rotation eps (float, optional): error threshold for clifford plus T decomposition of this operation Returns: CompressedResourceOp: the operator in a compressed representation """ return CompressedResourceOp(cls, {"pauli_string": pauli_string, "eps": eps})
[docs] @classmethod def default_adjoint_resource_decomp(cls, pauli_string, eps=None) -> list[GateCount]: r"""Returns a list representing the resources for the adjoint of the operator. Args: pauli_string (str): a string describing the pauli operators that define the rotation eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The adjoint of this operator just changes the sign of the phase, thus 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(pauli_string=pauli_string, eps=eps))]
[docs] @classmethod def default_controlled_resource_decomp( cls, ctrl_num_ctrl_wires, ctrl_num_ctrl_values, pauli_string, eps=None, ) -> 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 pauli_string (str): a string describing the pauli operators that define the rotation eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: When the :code:`pauli_string` is a single Pauli operator (:code:`X, Y, Z, Identity`) the cost is the associated controlled single qubit rotation gate: (:class:`~.ResourceCRX`, :class:`~.ResourceCRY`, :class:`~.ResourceCRZ`, controlled-:class:`~.ResourceGlobalPhase`). The resources are derived from the following identity. If an operation :math:`\hat{A}` can be expressed as :math:`\hat{A} \ = \ \hat{U} \cdot \hat{B} \cdot \hat{U}^{\dagger}` then the controlled operation :math:`C\hat{A}` can be expressed as: .. math:: C\hat{A} \ = \ \hat{U} \cdot C\hat{B} \cdot \hat{U}^{\dagger} Specifically, the resources are one multi-controlled RZ-gate and a cascade of :math:`2 * (n - 1)` :class:`~.ResourceCNOT` gates where :math:`n` is the number of qubits the gate acts on. Additionally, for each :code:`X` gate in the Pauli word we conjugate by a pair of :class:`~.ResourceHadamard` gates, and for each :code:`Y` gate in the Pauli word we conjugate by a pair of :class:`~.ResourceHadamard` and a pair of :class:`~.ResourceS` gates. 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 (set(pauli_string) == {"I"}) or (len(pauli_string) == 0): ctrl_gp = re.ResourceControlled.resource_rep( re.resource_rep(re.ResourceGlobalPhase), ctrl_num_ctrl_wires, ctrl_num_ctrl_values, ) return [GateCount(ctrl_gp)] if pauli_string == "X": return [ GateCount( re.ResourceControlled.resource_rep( re.resource_rep(re.ResourceRX, {"eps": eps}), ctrl_num_ctrl_wires, ctrl_num_ctrl_values, ) ) ] if pauli_string == "Y": return [ GateCount( re.ResourceControlled.resource_rep( re.resource_rep(re.ResourceRY, {"eps": eps}), ctrl_num_ctrl_wires, ctrl_num_ctrl_values, ) ) ] if pauli_string == "Z": return [ GateCount( re.ResourceControlled.resource_rep( re.resource_rep(re.ResourceRZ, {"eps": eps}), ctrl_num_ctrl_wires, ctrl_num_ctrl_values, ) ) ] active_wires = len(pauli_string.replace("I", "")) h = re.ResourceHadamard.resource_rep() s = re.ResourceS.resource_rep() crz = re.ResourceControlled.resource_rep( re.resource_rep(re.ResourceRZ, {"eps": eps}), ctrl_num_ctrl_wires, ctrl_num_ctrl_values, ) s_dagg = re.resource_rep( re.ResourceAdjoint, {"base_cmpr_op": re.resource_rep(re.ResourceS)}, ) cnot = re.ResourceCNOT.resource_rep() h_count = 0 s_count = 0 for gate in pauli_string: if gate == "X": h_count += 1 if gate == "Y": h_count += 1 s_count += 1 gate_types = [] if h_count: gate_types.append(GateCount(h, 2 * h_count)) if s_count: gate_types.append(GateCount(s, s_count)) gate_types.append(GateCount(s_dagg, s_count)) gate_types.append(GateCount(crz)) gate_types.append(GateCount(cnot, 2 * (active_wires - 1))) return gate_types
[docs] @classmethod def default_pow_resource_decomp(cls, pow_z, pauli_string, 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 pauli_string (str): a string describing the pauli operators that define the rotation eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: Taking arbitrary powers of a general rotation produces a sum of rotations. The resources simplify to just one total pauli 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(pauli_string=pauli_string, eps=eps))]
[docs] class ResourceIsingXX(ResourceOperator): r"""Resource class for the IsingXX gate. Args: eps (float, optional): error threshold for Clifford+T decomposition of this operation wires (Sequence[int], optional): the wire the operation acts on Resources: Ising XX coupling gate .. math:: XX(\phi) = \exp\left(-i \frac{\phi}{2} (X \otimes X)\right) = \begin{bmatrix} = \cos(\phi / 2) & 0 & 0 & -i \sin(\phi / 2) \\ 0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\ 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ -i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2) \end{bmatrix}. The circuit implementing this transformation is given by: .. code-block:: bash 0: ─╭●─────RX────╭●─┤ 1: ─╰X───────────╰X─┤ .. seealso:: :class:`~.IsingXX` **Example** The resources for this operation are computed using: >>> ising_xx = plre.ResourceIsingXX() >>> gate_set = {"CNOT", "RX"} >>> print(plre.estimate_resources(ising_xx, gate_set)) --- Resources: --- Total qubits: 2 Total gates : 3 Qubit breakdown: clean qubits: 0, dirty qubits: 0, algorithmic qubits: 2 Gate breakdown: {'CNOT': 2, 'RX': 1} """ num_wires = 2 resource_keys = {"eps"} def __init__(self, eps=None, wires=None) -> None: self.eps = eps super().__init__(wires=wires)
[docs] @classmethod def default_resource_decomp(cls, eps=None, **kwargs): r"""Returns a list of GateCount objects representing the operator's resources. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: Ising XX coupling gate .. math:: XX(\phi) = \exp\left(-i \frac{\phi}{2} (X \otimes X)\right) = \begin{bmatrix} = \cos(\phi / 2) & 0 & 0 & -i \sin(\phi / 2) \\ 0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\ 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ -i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2) \end{bmatrix}. The cost for implementing this transformation is given by: .. code-block:: bash 0: ─╭●─────RX────╭●─┤ 1: ─╰X───────────╰X─┤ 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. """ cnot = re.ResourceCNOT.resource_rep() rx = re.ResourceRX.resource_rep(eps=eps) return [GateCount(cnot, 2), GateCount(rx)]
@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: * eps (float): error threshold for clifford plus T decomposition of this operation """ return {"eps": self.eps}
[docs] @classmethod def resource_rep(cls, eps=None): """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Returns: CompressedResourceOp: the operator in a compressed representation """ return CompressedResourceOp(cls, {"eps": eps})
[docs] @classmethod def default_adjoint_resource_decomp(cls, eps=None) -> list[GateCount]: r"""Returns a list representing the resources for the adjoint of the operator. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The adjoint of this operator just changes the sign of the phase angle, thus 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(eps=eps))]
[docs] @classmethod def default_controlled_resource_decomp( cls, ctrl_num_ctrl_wires, ctrl_num_ctrl_values, eps=None, ) -> 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 eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The resources are derived from the following identity. If an operation :math:`\hat{A}` can be expressed as :math:`\hat{A} \ = \ \hat{U} \cdot \hat{B} \cdot \hat{U}^{\dagger}` then the controlled operation :math:`C\hat{A}` can be expressed as: .. math:: C\hat{A} \ = \ \hat{U} \cdot C\hat{B} \cdot \hat{U}^{\dagger} Specifically, the resources are one multi-controlled RX-gate and a pair of :class:`~.ResourceCNOT` gates. 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. """ cnot = re.ResourceCNOT.resource_rep() ctrl_rx = re.ResourceControlled.resource_rep( base_cmpr_op=re.ResourceRX.resource_rep(eps=eps), num_ctrl_wires=ctrl_num_ctrl_wires, num_ctrl_values=ctrl_num_ctrl_values, ) return [GateCount(cnot, 2), GateCount(ctrl_rx)]
[docs] @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): error threshold for clifford plus T decomposition of this operation Resources: Taking arbitrary powers of a rotation produces a sum of rotations. The resources simplify to just one total Ising 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=eps))]
[docs] class ResourceIsingYY(ResourceOperator): r"""Resource class for the IsingYY gate. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation wires (Sequence[int], optional): the wire the operation acts on Resources: Ising YY coupling gate .. math:: \mathtt{YY}(\phi) = \exp\left(-i \frac{\phi}{2} (Y \otimes Y)\right) = \begin{bmatrix} \cos(\phi / 2) & 0 & 0 & i \sin(\phi / 2) \\ 0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\ 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2) \end{bmatrix}. The cost for implementing this transformation is given by: .. code-block:: bash 0: ─╭●─────RY────╭●─┤ 1: ─╰Y───────────╰Y─┤ .. seealso:: :class:`~.IsingYY` **Example** The resources for this operation are computed using: >>> ising_yy = plre.ResourceIsingYY() >>> gate_set = {"CY", "RY"} >>> print(plre.estimate_resources(ising_yy, gate_set)) --- Resources: --- Total qubits: 2 Total gates : 3 Qubit breakdown: clean qubits: 0, dirty qubits: 0, algorithmic qubits: 2 Gate breakdown: {'CY': 2, 'RY': 1} """ num_wires = 2 resource_keys = {"eps"} def __init__(self, eps=None, wires=None) -> None: self.eps = eps super().__init__(wires=wires)
[docs] @classmethod def default_resource_decomp(cls, eps=None, **kwargs): r"""Returns a list of GateCount objects representing the operator's resources. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: Ising YY coupling gate .. math:: \mathtt{YY}(\phi) = \exp\left(-i \frac{\phi}{2} (Y \otimes Y)\right) = \begin{bmatrix} \cos(\phi / 2) & 0 & 0 & i \sin(\phi / 2) \\ 0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\ 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2) \end{bmatrix}. The cost for implementing this transformation is given by: .. code-block:: bash 0: ─╭●─────RY────╭●─┤ 1: ─╰Y───────────╰Y─┤ 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. """ cy = re.ops.ResourceCY.resource_rep() ry = re.ops.ResourceRY.resource_rep(eps=eps) return [GateCount(cy, 2), GateCount(ry)]
@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: * eps (float): error threshold for clifford plus T decomposition of this operation """ return {"eps": self.eps}
[docs] @classmethod def resource_rep(cls, eps=None): """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Returns: CompressedResourceOp: the operator in a compressed representation """ return CompressedResourceOp(cls, {"eps": eps})
[docs] @classmethod def default_adjoint_resource_decomp(cls, eps=None) -> list[GateCount]: r"""Returns a list representing the resources for the adjoint of the operator. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The adjoint of this operator just changes the sign of the phase angle, thus 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(eps=eps))]
[docs] @classmethod def default_controlled_resource_decomp( cls, ctrl_num_ctrl_wires, ctrl_num_ctrl_values, eps=None, ) -> 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 eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The resources are derived from the following identity. If an operation :math:`\hat{A}` can be expressed as :math:`\hat{A} \ = \ \hat{U} \cdot \hat{B} \cdot \hat{U}^{\dagger}` then the controlled operation :math:`C\hat{A}` can be expressed as: .. math:: C\hat{A} \ = \ \hat{U} \cdot C\hat{B} \cdot \hat{U}^{\dagger} Specifically, the resources are one multi-controlled RY-gate and a pair of :class:`~.ResourceCY` gates. 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. """ cy = re.ops.ResourceCY.resource_rep() ctrl_ry = re.ResourceControlled.resource_rep( base_cmpr_op=re.ResourceRY.resource_rep(eps=eps), num_ctrl_wires=ctrl_num_ctrl_wires, num_ctrl_values=ctrl_num_ctrl_values, ) return [GateCount(cy, 2), GateCount(ctrl_ry)]
[docs] @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): error threshold for clifford plus T decomposition of this operation Resources: Taking arbitrary powers of a rotation produces a sum of rotations. The resources simplify to just one total Ising 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=eps))]
[docs] class ResourceIsingXY(ResourceOperator): r"""Resource class for the IsingXY gate. Args: eps (float, optional): error threshold for Clifford+T decomposition of this operation wires (Sequence[int], optional): the wire the operation acts on Resources: Ising (XX + YY) coupling gate .. math:: \mathtt{XY}(\phi) = \exp\left(i \frac{\theta}{4} (X \otimes X + Y \otimes Y)\right) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos(\phi / 2) & i \sin(\phi / 2) & 0 \\ 0 & i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}. The cost for implementing this transformation is given by: .. code-block:: bash 0: ──H─╭●─────RY────╭●──H─┤ 1: ────╰Y─────RX────╰Y────┤ .. seealso:: :class:`~.IsingXY` **Example** The resources for this operation are computed using: >>> ising_xy = plre.ResourceIsingXY() >>> gate_set = {"Hadamard", "CY", "RY", "RX"} >>> print(plre.estimate_resources(ising_xy, gate_set)) --- Resources: --- Total qubits: 2 Total gates : 6 Qubit breakdown: clean qubits: 0, dirty qubits: 0, algorithmic qubits: 2 Gate breakdown: {'Hadamard': 2, 'CY': 2, 'RY': 1, 'RX': 1} """ num_wires = 2 resource_keys = {"eps"} def __init__(self, eps=None, wires=None) -> None: self.eps = eps super().__init__(wires=wires)
[docs] @classmethod def default_resource_decomp(cls, eps=None, **kwargs): r"""Returns a list of GateCount objects representing the operator's resources. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: IsingXY coupling gate .. math:: \mathtt{XY}(\phi) = \exp\left(i \frac{\theta}{4} (X \otimes X + Y \otimes Y)\right) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos(\phi / 2) & i \sin(\phi / 2) & 0 \\ 0 & i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}. The cost for implementing this transformation is given by: .. code-block:: bash 0: ──H─╭●─────RY────╭●──H─┤ 1: ────╰Y─────RX────╰Y────┤ 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 = re.ResourceHadamard.resource_rep() cy = re.ResourceCY.resource_rep() ry = re.ResourceRY.resource_rep(eps=eps) rx = re.ResourceRX.resource_rep(eps=eps) return [GateCount(h, 2), GateCount(cy, 2), GateCount(ry), GateCount(rx)]
@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: * eps (float): error threshold for clifford plus T decomposition of this operation """ return {"eps": self.eps}
[docs] @classmethod def resource_rep(cls, eps=None): """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Returns: CompressedResourceOp: the operator in a compressed representation """ return CompressedResourceOp(cls, {"eps": eps})
[docs] @classmethod def default_adjoint_resource_decomp(cls, eps=None) -> list[GateCount]: r"""Returns a list representing the resources for the adjoint of the operator. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The adjoint of this operator just changes the sign of the phase angle, thus 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(eps=eps))]
[docs] @classmethod def default_controlled_resource_decomp( cls, ctrl_num_ctrl_wires, ctrl_num_ctrl_values, eps=None, ) -> 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 eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The resources are derived from the following identity. If an operation :math:`\hat{A}` can be expressed as :math:`\hat{A} \ = \ \hat{U} \cdot \hat{B} \cdot \hat{U}^{\dagger}` then the controlled operation :math:`C\hat{A}` can be expressed as: .. math:: C\hat{A} \ = \ \hat{U} \cdot C\hat{B} \cdot \hat{U}^{\dagger} Specifically, the resources are one multi-controlled RY-gate, one multi-controlled RX-gate, a pair of :class:`~.ResourceCY` gates and a pair of :class:`~.ResourceHadamard` gates. 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 = re.ResourceHadamard.resource_rep() cy = re.ResourceCY.resource_rep() ctrl_rx = re.ResourceControlled.resource_rep( base_cmpr_op=re.ResourceRX.resource_rep(eps=eps), num_ctrl_wires=ctrl_num_ctrl_wires, num_ctrl_values=ctrl_num_ctrl_values, ) ctrl_ry = re.ResourceControlled.resource_rep( base_cmpr_op=re.ResourceRY.resource_rep(eps=eps), num_ctrl_wires=ctrl_num_ctrl_wires, num_ctrl_values=ctrl_num_ctrl_values, ) return [GateCount(h, 2), GateCount(cy, 2), GateCount(ctrl_ry), GateCount(ctrl_rx)]
[docs] @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): error threshold for clifford plus T decomposition of this operation Resources: Taking arbitrary powers of a rotation produces a sum of rotations. The resources simplify to just one total Ising 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=eps))]
[docs] class ResourceIsingZZ(ResourceOperator): r"""Resource class for the IsingZZ gate. Args: eps (float, optional): error threshold for Clifford+T decomposition of this operation wires (Sequence[int], optional): the wire the operation acts on Resources: Ising ZZ coupling gate .. math:: ZZ(\phi) = \exp\left(-i \frac{\phi}{2} (Z \otimes Z)\right) = \begin{bmatrix} e^{-i \phi / 2} & 0 & 0 & 0 \\ 0 & e^{i \phi / 2} & 0 & 0 \\ 0 & 0 & e^{i \phi / 2} & 0 \\ 0 & 0 & 0 & e^{-i \phi / 2} \end{bmatrix}. The cost for implmenting this transformation is given by: .. code-block:: bash 0: ─╭●───────────╭●─┤ 1: ─╰X─────RZ────╰X─┤ .. seealso:: :class:`~.IsingZZ` **Example** The resources for this operation are computed using: >>> ising_zz = plre.ResourceIsingZZ() >>> gate_set = {"CNOT", "RZ"} >>> print(plre.estimate_resources(ising_zz, gate_set)) --- Resources: --- Total qubits: 2 Total gates : 3 Qubit breakdown: clean qubits: 0, dirty qubits: 0, algorithmic qubits: 2 Gate breakdown: {'CNOT': 2, 'RZ': 1} """ num_wires = 2 resource_keys = {"eps"} def __init__(self, eps=None, wires=None) -> None: self.eps = eps super().__init__(wires=wires)
[docs] @classmethod def default_resource_decomp(cls, eps=None, **kwargs): r"""Returns a list of GateCount objects representing the operator's resources. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: Ising ZZ coupling gate .. math:: ZZ(\phi) = \exp\left(-i \frac{\phi}{2} (Z \otimes Z)\right) = \begin{bmatrix} e^{-i \phi / 2} & 0 & 0 & 0 \\ 0 & e^{i \phi / 2} & 0 & 0 \\ 0 & 0 & e^{i \phi / 2} & 0 \\ 0 & 0 & 0 & e^{-i \phi / 2} \end{bmatrix}. The cost for implmenting this transformation is given by: .. code-block:: bash 0: ─╭●───────────╭●─┤ 1: ─╰X─────RZ────╰X─┤ 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. """ cnot = re.ResourceCNOT.resource_rep() rz = re.ResourceRZ.resource_rep(eps=eps) gate_types = {} gate_types[cnot] = 2 gate_types[rz] = 1 return [GateCount(cnot, 2), GateCount(rz)]
@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: * eps (float): error threshold for clifford plus T decomposition of this operation """ return {"eps": self.eps}
[docs] @classmethod def resource_rep(cls, eps=None): """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Returns: CompressedResourceOp: the operator in a compressed representation """ return CompressedResourceOp(cls, {"eps": eps})
[docs] @classmethod def default_adjoint_resource_decomp(cls, eps=None) -> list[GateCount]: r"""Returns a list representing the resources for the adjoint of the operator. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The adjoint of this operator just changes the sign of the phase angle, thus 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(eps=eps))]
[docs] @classmethod def default_controlled_resource_decomp( cls, ctrl_num_ctrl_wires, ctrl_num_ctrl_values, eps=None, ) -> 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 eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The resources are derived from the following identity. If an operation :math:`\hat{A}` can be expressed as :math:`\hat{A} \ = \ \hat{U} \cdot \hat{B} \cdot \hat{U}^{\dagger}` then the controlled operation :math:`C\hat{A}` can be expressed as: .. math:: C\hat{A} \ = \ \hat{U} \cdot C\hat{B} \cdot \hat{U}^{\dagger} Specifically, the resources are one multi-controlled RZ-gate and a pair of :class:`~.ResourceCNOT` gates. 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. """ cnot = re.ResourceCNOT.resource_rep() ctrl_rz = re.ResourceControlled.resource_rep( base_cmpr_op=re.ResourceRZ.resource_rep(eps=eps), num_ctrl_wires=ctrl_num_ctrl_wires, num_ctrl_values=ctrl_num_ctrl_values, ) return [GateCount(cnot, 2), GateCount(ctrl_rz)]
[docs] @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): error threshold for clifford plus T decomposition of this operation Resources: Taking arbitrary powers of a rotation produces a sum of rotations. The resources simplify to just one total Ising 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=eps))]
[docs] class ResourcePSWAP(ResourceOperator): r"""Resource class for the PSWAP gate. Args: eps (float, optional): error threshold for Clifford+T decomposition of this operation wires (Sequence[int], optional): the wire the operation acts on Resources: The :code:`PSWAP` gate is defined as: .. math:: PSWAP(\phi) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & e^{i \phi} & 0 \\ 0 & e^{i \phi} & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}. The cost for implementing this transformation is given by: .. code-block:: bash 0: ─╭SWAP─╭●───────────╭●─┤ 1: ─╰SWAP─╰X─────Rϕ────╰X─┤ .. seealso:: :class:`~.PSWAP` **Example** The resources for this operation are computed using: >>> pswap = plre.ResourcePSWAP() >>> gate_set = {"CNOT", "SWAP", "PhaseShift"} >>> print(plre.estimate_resources(pswap, gate_set)) --- Resources: --- Total qubits: 2 Total gates : 4 Qubit breakdown: clean qubits: 0, dirty qubits: 0, algorithmic qubits: 2 Gate breakdown: {'SWAP': 1, 'PhaseShift': 1, 'CNOT': 2} """ num_wires = 2 resource_keys = {"eps"} def __init__(self, eps=None, wires=None) -> None: self.eps = eps super().__init__(wires=wires)
[docs] @classmethod def default_resource_decomp(cls, eps=None, **kwargs): r"""Returns a list of GateCount objects representing the operator's resources. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The :code:`PSWAP` gate is defined as: .. math:: PSWAP(\phi) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & e^{i \phi} & 0 \\ 0 & e^{i \phi} & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}. The cost for implementing this transformation is given by: .. code-block:: bash 0: ─╭SWAP─╭●───────────╭●─┤ 1: ─╰SWAP─╰X─────Rϕ────╰X─┤ 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. """ swap = re.ResourceSWAP.resource_rep() cnot = re.ResourceCNOT.resource_rep() phase = re.ResourcePhaseShift.resource_rep(eps=eps) return [GateCount(swap), GateCount(phase), GateCount(cnot, 2)]
@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: * eps (float): error threshold for clifford plus T decomposition of this operation """ return {"eps": self.eps}
[docs] @classmethod def resource_rep(cls, eps=None): """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Returns: CompressedResourceOp: the operator in a compressed representation """ return CompressedResourceOp(cls, {"eps": eps})
[docs] @classmethod def default_adjoint_resource_decomp(cls, eps=None) -> list[GateCount]: r"""Returns a list representing the resources for the adjoint of the operator. Args: eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The adjoint of this operator just changes the sign of the phase angle, thus 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(eps=eps))]
[docs] @classmethod def default_controlled_resource_decomp( cls, ctrl_num_ctrl_wires, ctrl_num_ctrl_values, eps=None, ) -> 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 eps (float, optional): error threshold for clifford plus T decomposition of this operation Resources: The resources are derived from the following identity. If an operation :math:`\hat{A}` can be expressed as :math:`\hat{A} \ = \ \hat{U} \cdot \hat{B} \cdot \hat{U}^{\dagger}` then the controlled operation :math:`C\hat{A}` can be expressed as: .. math:: C\hat{A} \ = \ \hat{U} \cdot C\hat{B} \cdot \hat{U}^{\dagger} Specifically, the resources are one multi-controlled phase shift gate, one multi-controlled SWAP gate and a pair of :class:`~.ResourceCNOT` gates. 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. """ cnot = re.ResourceCNOT.resource_rep() ctrl_swap = re.ResourceControlled.resource_rep( base_cmpr_op=re.ResourceSWAP.resource_rep(), num_ctrl_wires=ctrl_num_ctrl_wires, num_ctrl_values=ctrl_num_ctrl_values, ) ctrl_ps = re.ResourceControlled.resource_rep( base_cmpr_op=re.ResourcePhaseShift.resource_rep(eps=eps), num_ctrl_wires=ctrl_num_ctrl_wires, num_ctrl_values=ctrl_num_ctrl_values, ) return [GateCount(ctrl_swap), GateCount(cnot, 2), GateCount(ctrl_ps)]