Source code for pennylane.estimator.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."""
from collections import Counter
import pennylane.estimator as qre
from pennylane.estimator.resource_operator import CompressedResourceOp, GateCount, ResourceOperator
from pennylane.math.decomposition import decomp_int_to_powers_of_two
from pennylane.wires import Wires, WiresLike
# pylint: disable=arguments-differ, signature-differs
PAULI_ROT_SPECIAL_CASES = {
"X": lambda eps: [GateCount(qre.resource_rep(qre.RX, {"precision": eps}))],
"Y": lambda eps: [GateCount(qre.resource_rep(qre.RY, {"precision": eps}))],
"Z": lambda eps: [GateCount(qre.resource_rep(qre.RZ, {"precision": eps}))],
"XX": lambda eps: [
GateCount(qre.resource_rep(qre.RX, {"precision": eps})),
GateCount(qre.resource_rep(qre.CNOT), count=2),
],
"YY": lambda eps: [
GateCount(qre.resource_rep(qre.RY, {"precision": eps})),
GateCount(qre.resource_rep(qre.CY), count=2),
],
}
[docs]
class MultiRZ(ResourceOperator):
r"""Resource class for the MultiRZ gate.
Args:
num_wires (int | None): the number of wires the operation acts upon
precision (float | None): error threshold for the Clifford + T decomposition of this operation
wires (WiresLike | None): 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/abs/1208.5986>`_ paper.
Specifically, the resources are given by one ``RZ`` gate and a cascade of
:math:`2 \times (n - 1)` ``CNOT`` gates where :math:`n` is the number of qubits
the gate acts on.
.. seealso:: The corresponding PennyLane operation :class:`~.pennylane.MultiRZ`.
**Example**
The resources for this operation are computed using:
>>> import pennylane.estimator as qre
>>> multi_rz = qre.MultiRZ(num_wires=3)
>>> gate_set = {"CNOT", "RZ"}
>>>
>>> print(qml.estimator.estimate(multi_rz, gate_set))
--- Resources: ---
Total wires: 3
algorithmic wires: 3
allocated wires: 0
zero state: 0
any state: 0
Total gates : 5
'RZ': 1,
'CNOT': 4
"""
resource_keys = {"num_wires", "precision"}
def __init__(
self, num_wires: int | None = None, precision: float | None = None, wires: WiresLike = None
) -> None:
if num_wires is None:
if wires is None:
raise ValueError("Must provide at least one of `num_wires` and `wires`.")
num_wires = len(wires)
self.num_wires = num_wires
self.precision = precision
if wires is not None and len(Wires(wires)) != self.num_wires:
raise ValueError(f"Expected {self.num_wires} wires, got {len(Wires(wires))}")
super().__init__(wires=wires)
[docs]
@classmethod
def resource_decomp(cls, num_wires: int, precision: float | None = None) -> list[GateCount]:
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
precision (float): error threshold for the Clifford + 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/abs/1208.5986>`_ paper.
Specifically, the resources are given by one ``RZ`` gate and a cascade of
:math:`2 \times (n - 1)` ``CNOT`` gates where :math:`n` is the number of qubits
the gate acts on.
Returns:
list[:class:`~.pennylane.estimator.resource_operator.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 = qre.CNOT.resource_rep()
rz = qre.RZ.resource_rep(precision=precision)
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
* precision (float): error threshold for the Clifford + T decomposition of this operation
"""
return {"num_wires": self.num_wires, "precision": self.precision}
[docs]
@classmethod
def resource_rep(cls, num_wires: int, precision: float | None = 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
precision (float): error threshold for the Clifford + T decomposition of this operation
Returns:
:class:`~.pennylane.estimator.resource_operator.CompressedResourceOp`: the operator in a compressed representation
"""
return CompressedResourceOp(
cls, num_wires, {"num_wires": num_wires, "precision": precision}
)
[docs]
@classmethod
def adjoint_resource_decomp(cls, target_resource_params: dict) -> list[GateCount]:
r"""Returns a list representing the resources for the adjoint of the operator.
Args:
target_resource_params (dict): A dictionary containing the resource parameters of the target operator
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[:class:`~.pennylane.estimator.resource_operator.GateCount`]: A list of ``GateCount`` objects,
where each object represents a specific quantum gate and the number of times it appears
in the decomposition.
"""
num_wires = target_resource_params["num_wires"]
precision = target_resource_params["precision"]
return [GateCount(cls.resource_rep(num_wires=num_wires, precision=precision))]
[docs]
@classmethod
def controlled_resource_decomp(
cls,
num_ctrl_wires: int,
num_zero_ctrl: int,
target_resource_params: dict | None = None,
) -> list[GateCount]:
r"""Returns a list representing the resources for a controlled version of the operator.
Args:
num_ctrl_wires (int): the number of qubits the operation is controlled on
num_zero_ctrl (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
target_resource_params (dict): A dictionary containing the resource parameters of the target operator
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)` ``CNOT`` gates where :math:`n` is the number of qubits
the gate acts on.
Returns:
list[:class:`~.pennylane.estimator.resource_operator.GateCount`]: A list of ``GateCount`` objects,
where each object represents a specific quantum gate and the number of times it appears
in the decomposition.
"""
num_wires = target_resource_params["num_wires"]
precision = target_resource_params["precision"]
cnot = qre.resource_rep(qre.CNOT)
ctrl_rz = qre.resource_rep(
qre.Controlled,
{
"base_cmpr_op": qre.resource_rep(qre.RZ, {"precision": precision}),
"num_ctrl_wires": num_ctrl_wires,
"num_zero_ctrl": num_zero_ctrl,
},
)
return [GateCount(cnot, 2 * (num_wires - 1)), GateCount(ctrl_rz)]
[docs]
@classmethod
def pow_resource_decomp(cls, pow_z: int, target_resource_params: dict) -> 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
target_resource_params (dict): A dictionary containing the resource parameters of the target operator.
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[:class:`~.pennylane.estimator.resource_operator.GateCount`]: A list of ``GateCount`` objects,
where each object represents a specific quantum gate and the number of times it appears
in the decomposition.
"""
num_wires = target_resource_params["num_wires"]
precision = target_resource_params["precision"]
return [GateCount(cls.resource_rep(num_wires=num_wires, precision=precision))]
[docs]
class PauliRot(ResourceOperator):
r"""Resource class for an arbitrary Pauli word rotation operation.
Args:
pauli_string (str): a string describing the Pauli operators that define the rotation
precision (float | None): error threshold for the Clifford + T decomposition of the operation
wires (WiresLike | None): the wire the operation acts on
Resources:
The resources are computed based on Section VIII (Figures 3 and 4) of
`The Bravyi-Kitaev transformation for quantum computation of electronic structure
<https://arxiv.org/abs/1208.5986>`_, in combination with the following identities:
.. 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}
Note that when the :code:`pauli_string` is a single Pauli operator (:code:`X, Y, Z, Identity`),
the cost is the associated single-qubit rotation (i.e., :code:`RX, RY, RZ, GlobalPhase`). If the
:code:`pauli_string` is :code:`XX`, the resources are one :code:`RX` gate at the specified
precision and two :code:`CNOT` gates. If the :code:`pauli_string` is :code:`YY`, the
resources are one :code:`RY` gate at the specified precision and two :code:`CY` gates.
.. seealso:: The corresponding PennyLane operation :class:`~.pennylane.PauliRot`.
**Example**
The resources for this operation are computed using:
>>> import pennylane.estimator as qre
>>> pr = qre.PauliRot(pauli_string="XYZ")
>>> print(qre.estimate(pr))
--- Resources: ---
Total wires: 3
algorithmic wires: 3
allocated wires: 0
zero state: 0
any state: 0
Total gates : 55
'T': 44,
'CNOT': 4,
'Z': 1,
'S': 2,
'Hadamard': 4
"""
resource_keys = {"pauli_string", "precision"}
def __init__(
self, pauli_string: str, precision: float | None = None, wires: WiresLike = None
) -> None:
self.precision = precision
self.pauli_string = pauli_string
self.num_wires = len(pauli_string)
if wires is not None and len(Wires(wires)) != self.num_wires:
raise ValueError(f"Expected {self.num_wires} wires, got {len(Wires(wires))}")
super().__init__(wires=wires)
[docs]
@classmethod
def resource_decomp(cls, pauli_string: str, precision: float | None = None) -> list[GateCount]:
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
precision (float | None): error threshold for the Clifford + T decomposition of this operation
Resources:
The resources are computed based on Section VIII (Figures 3 and 4) of
`The Bravyi-Kitaev transformation for quantum computation of electronic structure
<https://arxiv.org/abs/1208.5986>`_, in combination with the following identities:
.. 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}
Note that when the :code:`pauli_string` is a single Pauli operator (:code:`X, Y, Z, Identity`),
the cost is the associated single-qubit rotation (i.e., :code:`RX, RY, RZ, GlobalPhase`). If the
:code:`pauli_string` is :code:`XX`, the resources are one :code:`RX` gate at the specified
precision and two :code:`CNOT` gates. If the :code:`pauli_string` is :code:`YY`, the
resources are one :code:`RY` gate at the specified precision and two :code:`CY` gates.
Returns:
list[:class:`~.pennylane.estimator.resource_operator.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):
return [GateCount(qre.resource_rep(qre.GlobalPhase))]
# Special cases:
if pauli_string in PAULI_ROT_SPECIAL_CASES:
return PAULI_ROT_SPECIAL_CASES[pauli_string](eps=precision)
active_wires = len(pauli_string.replace("I", ""))
h = qre.resource_rep(qre.Hadamard)
s = qre.resource_rep(qre.S)
rz = qre.resource_rep(qre.RZ, {"precision": precision})
s_dagg = qre.resource_rep(
qre.Adjoint,
{"base_cmpr_op": qre.resource_rep(qre.S)},
)
cnot = qre.resource_rep(qre.CNOT)
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
* precision (float): error threshold for the Clifford + T decomposition of this operation
"""
return {
"pauli_string": self.pauli_string,
"precision": self.precision,
}
[docs]
@classmethod
def resource_rep(cls, pauli_string: str, precision: float | None = 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
precision (float | None): error threshold for the Clifford + T decomposition of this operation
Returns:
:class:`~.pennylane.estimator.resource_operator.CompressedResourceOp`:: the operator in a compressed representation
"""
num_wires = len(pauli_string)
return CompressedResourceOp(
cls, num_wires, {"pauli_string": pauli_string, "precision": precision}
)
[docs]
@classmethod
def adjoint_resource_decomp(cls, target_resource_params: dict) -> list[GateCount]:
r"""Returns a list representing the resources for the adjoint of the operator.
Args:
target_resource_params (dict): A dictionary containing the resource parameters of the target operator
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[:class:`~.pennylane.estimator.resource_operator.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 = target_resource_params["precision"]
pauli_string = target_resource_params["pauli_string"]
return [GateCount(cls.resource_rep(pauli_string=pauli_string, precision=precision))]
[docs]
@classmethod
def controlled_resource_decomp(
cls,
num_ctrl_wires: int,
num_zero_ctrl: int,
target_resource_params: dict,
) -> list[GateCount]:
r"""Returns a list representing the resources for a controlled version of the operator.
Args:
num_ctrl_wires (int): the number of qubits the operation is controlled on
num_zero_ctrl (int): the number of control qubits, that are controlled when in the :math:`|0\rangle` state
target_resource_params (dict): A dictionary containing the resource parameters of the target operator
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: (:code:`CRX`,
:code:`CRY`, :code:`CRZ`, controlled-\ :code:`GlobalPhase`).
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 \times (n - 1)` :code:`CNOT` 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 :code:`Hadamard` gates, and for each :code:`Y` gate in the Pauli word
we conjugate by a pair of :code:`Hadamard` and a pair of :code:`S` gates.
Returns:
list[:class:`~.pennylane.estimator.resource_operator.GateCount`]: A list of ``GateCount`` objects,
where each object represents a specific quantum gate and the number of times it appears
in the decomposition.
"""
pauli_string = target_resource_params["pauli_string"]
precision = target_resource_params["precision"]
if (set(pauli_string) == {"I"}) or (len(pauli_string) == 0):
ctrl_gp = qre.Controlled.resource_rep(
qre.resource_rep(qre.GlobalPhase),
num_ctrl_wires,
num_zero_ctrl,
)
return [GateCount(ctrl_gp)]
if pauli_string == "X":
return [
GateCount(
qre.Controlled.resource_rep(
qre.resource_rep(qre.RX, {"precision": precision}),
num_ctrl_wires,
num_zero_ctrl,
)
)
]
if pauli_string == "Y":
return [
GateCount(
qre.Controlled.resource_rep(
qre.resource_rep(qre.RY, {"precision": precision}),
num_ctrl_wires,
num_zero_ctrl,
)
)
]
if pauli_string == "Z":
return [
GateCount(
qre.Controlled.resource_rep(
qre.resource_rep(qre.RZ, {"precision": precision}),
num_ctrl_wires,
num_zero_ctrl,
)
)
]
active_wires = len(pauli_string.replace("I", ""))
h = qre.Hadamard.resource_rep()
s = qre.S.resource_rep()
crz = qre.Controlled.resource_rep(
qre.resource_rep(qre.RZ, {"precision": precision}),
num_ctrl_wires,
num_zero_ctrl,
)
s_dagg = qre.resource_rep(
qre.Adjoint,
{"base_cmpr_op": qre.resource_rep(qre.S)},
)
cnot = qre.CNOT.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 pow_resource_decomp(cls, pow_z: int, target_resource_params: dict) -> 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
target_resource_params (dict): A dictionary containing the resource parameters of the target operator.
Resources:
Taking arbitrary powers of a general rotation produces a sum of rotations.
The resources simplify to just one total Pauli rotation.
Returns:
list[:class:`~.pennylane.estimator.resource_operator.GateCount`]: A list of ``GateCount`` objects,
where each object represents a specific quantum gate and the number of times it appears
in the decomposition.
"""
pauli_string = target_resource_params["pauli_string"]
precision = target_resource_params["precision"]
return [GateCount(cls.resource_rep(pauli_string=pauli_string, precision=precision))]
[docs]
class PCPhase(ResourceOperator):
r"""A resource operator representing a projector-controlled phase gate.
This gate applies a complex phase :math:`e^{i\phi}` to the first ``dim``
basis vectors of the input state while applying a complex phase :math:`e^{-i \phi}`
to the remaining basis vectors. For example, consider the 2-qubit case where ``dim = 3``:
.. math:: \Pi(\phi) = \begin{bmatrix}
e^{i\phi} & 0 & 0 & 0 \\
0 & e^{i\phi} & 0 & 0 \\
0 & 0 & e^{i\phi} & 0 \\
0 & 0 & 0 & e^{-i\phi}
\end{bmatrix}.
This can also be written as :math:`\Pi(\phi) = \exp(i\phi(2\Pi-\mathbb{I}_N))`, where
:math:`N=2^n` is the Hilbert space dimension for :math:`n` qubits and :math:`\Pi` is
the diagonal projector with ``dim`` ones and ``N-dim`` zeros.
Args:
num_wires (int): the number of wires this operator acts on
dim (int): the dimension of the target subspace
rotation_precision (float | None): The error threshold for the approximate Clifford + T
decomposition of the :class:`~pennylane.estimator.ops.qubit.parametric_ops_single_qubit.PhaseShift` gates used to implement this operation.
wires (WiresLike | None): the wire the operation acts on
Resources:
The resources are derived from the decomposition of the generator :math:`G` of the
``PCPhase`` gate into multiple projectors, which generate (multi-controlled) ``PhaseShift`` gates,
potentially complemented with (non-controlled) ``X`` gates and/or a global phase.
The generator is given as :math:`G = 2 \Pi - \mathbb{I}_N`, where :math:`\Pi` is a projector.
The projector :math:`\Pi` is decomposed into sums and differences of powers of two,
which correspond to multi-controlled :class:`~pennylane.estimator.ops.qubit.parametric_ops_single_qubit.PhaseShift` gates.
.. seealso:: The corresponding PennyLane operation :class:`~.pennylane.PCPhase`.
**Example**
The resources for this operation are computed using:
>>> import pennylane.estimator as qre
>>> pc_phase = qre.PCPhase(num_wires=2, dim=2, rotation_precision=1e-5)
>>> print(qre.estimate(pc_phase))
--- Resources: ---
Total wires: 2
algorithmic wires: 2
allocated wires: 0
zero state: 0
any state: 0
Total gates : 28
'T': 28
"""
resource_keys = {"num_wires", "dim", "rotation_precision"}
def __init__(
self,
num_wires: int,
dim: int,
rotation_precision: float | None = None,
wires: WiresLike | None = None,
) -> None:
self.num_wires = num_wires
self.dim = dim
self.rotation_precision = rotation_precision
if wires is not None and len(Wires(wires)) != self.num_wires:
raise ValueError(f"Expected {self.num_wires} wires, got {len(Wires(wires))}")
super().__init__(wires=wires)
[docs]
@classmethod
def resource_decomp(
cls, num_wires: int, dim: int, rotation_precision: float | None = None
) -> list[GateCount]:
r"""Returns a list of GateCount objects representing the operator's resources.
Args:
num_wires (int): the number of wires this operator acts on
dim (int): the dimension of the target subspace
rotation_precision (float | None): The error threshold for the approximate Clifford + T
decomposition of the :class:`~pennylane.estimator.ops.qubit.parametric_ops_single_qubit.PhaseShift` gates used to implement this operation.
Resources:
The resources are derived from the decomposition of the generator :math:`G` of the
``PCPhase`` gate into multiple projectors, which generate (multi-controlled) :class:`~pennylane.estimator.ops.qubit.parametric_ops_single_qubit.PhaseShift` gates,
potentially complemented with (non-controlled) ``X`` gates and/or a global phase.
The generator is given as :math:`G = 2 \Pi - \mathbb{I}_N`, where :math:`\Pi` is a projector.
The projector :math:`\Pi` is decomposed into sums and differences of powers of two,
which correspond to multi-controlled :class:`~pennylane.estimator.ops.qubit.parametric_ops_single_qubit.PhaseShift` gates.
Returns:
list[:class:`~.pennylane.estimator.resource_operator.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_count = Counter()
flipped, *powers_of_two = decomp_int_to_powers_of_two(dim, num_wires + 1)
sigma = (-1) ** flipped
powers_of_two = [sigma * val for val in powers_of_two]
n_zero_control_values = 0
for i, c_i in enumerate(powers_of_two):
if c_i != 0:
subspace = int(c_i < 0)
if flipped:
subspace = 1 - subspace
gate_count.update(
cls._ctrl_phase_shift_resource(
subspace,
n_control_wires=i,
n_zero_control_values=n_zero_control_values,
rotation_precision=rotation_precision,
)
)
d_i = next(iter(val for val in powers_of_two[i + 1 :] if val != 0), None)
next_cval = d_i == 1
if c_i == 0:
next_cval = not next_cval
if flipped:
next_cval = not next_cval
if not next_cval:
n_zero_control_values += 1
gate_count[qre.GlobalPhase.resource_rep()] += 1
gate_count_lst = [GateCount(op, count) for op, count in gate_count.items()]
return gate_count_lst
@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 wires this operator acts on
* dim (int): the dimension of the target subspace
* rotation_precision(float | None): The error threshold for the approximate Clifford + T
decomposition of the :class:`~pennylane.estimator.ops.qubit.parametric_ops_single_qubit.PhaseShift` gates used to implement this operation.
"""
return {
"num_wires": self.num_wires,
"dim": self.dim,
"rotation_precision": self.rotation_precision,
}
[docs]
@classmethod
def resource_rep(cls, num_wires: int, dim: int, rotation_precision: float | None = 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 wires this operator acts on
dim (int): the dimension of the target subspace
rotation_precision(float | None): The error threshold for the approximate Clifford + T
decomposition of the :class:`~pennylane.estimator.ops.qubit.parametric_ops_single_qubit.PhaseShift` gates used to implement this operation.
Returns:
:class:`~.pennylane.estimator.resource_operator.CompressedResourceOp`: the operator in a compressed representation
"""
params = {"num_wires": num_wires, "dim": dim, "rotation_precision": rotation_precision}
return CompressedResourceOp(cls, num_wires, params)
@staticmethod
def _ctrl_phase_shift_resource(
subspace: int,
n_control_wires: int,
n_zero_control_values: int,
rotation_precision: float | None,
) -> dict[CompressedResourceOp, int]:
"""Returns the resources for a (multi-)controlled :class:`~pennylane.estimator.ops.qubit.parametric_ops_single_qubit.PhaseShift` gate.
Args:
subspace (int): The subspace indicator (0 or 1) determining whether X gates are needed.
n_control_wires (int): The number of control wires for the controlled :class:`~pennylane.estimator.ops.qubit.parametric_ops_single_qubit.PhaseShift`.
n_zero_control_values (int): The number of control qubits controlled on the |0⟩ state.
rotation_precision (float | None): The error threshold for the approximate Clifford + T
decomposition of the :class:`~pennylane.estimator.ops.qubit.parametric_ops_single_qubit.PhaseShift` gate.
"""
x = qre.X.resource_rep()
phase_shift = qre.PhaseShift.resource_rep(rotation_precision)
if n_control_wires == 0:
return {phase_shift: 1}
return {
qre.Controlled.resource_rep(
base_cmpr_op=phase_shift,
num_ctrl_wires=n_control_wires,
num_zero_ctrl=n_zero_control_values,
): 1,
x: 2 * (1 - subspace),
}
_modules/pennylane/estimator/ops/qubit/parametric_ops_multi_qubit
Download Python script
Download Notebook
View on GitHub