Source code for pennylane.templates.subroutines.select_pauli_rot
# Copyright 2018-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"""
Contains the SelectPauliRot template.
"""
import pennylane as qml
from pennylane.decomposition import add_decomps, adjoint_resource_rep, register_resources
from pennylane.operation import Operation
from pennylane.templates.state_preparations.mottonen import _apply_uniform_rotation_dagger
[docs]
class SelectPauliRot(Operation):
r"""Applies individual single-qubit Pauli rotations depending on the state of
designated control qubits.
This operator, also called a **multiplexed rotation** or **uniformly controlled rotation**,
applies a sequence of multi-controlled rotations about the same axis to a single target qubit.
The rotation angles are selected based on the state of the control qubits.
Its definition is given by:
.. math::
\sum_i | i \rangle \langle i | \otimes R_P(\alpha_i),
where :math:`| i \rangle` refers to the computational basis state of the control register,
the :math:`\{\alpha_i\}` are the rotation angles, and :math:`R_P` denotes a Pauli rotation
about the Pauli operator :math:`P` applied to the target qubit.
.. figure:: ../../../doc/_static/templates/subroutines/select_pauli_rot.png
:align: center
:width: 70%
:target: javascript:void(0);
For more details, see `Möttönen and Vartiainen (2005), Fig 7a <https://arxiv.org/abs/quant-ph/0504100>`_.
Args:
angles (tensor_like): The rotation angles to be applied. The length of the angles array must
be :math:`2^n`, where :math:`n` is the number of ``control_wires``.
control_wires (Sequence[int]): The control qubits used to select the rotation.
target_wire (Sequence[int]): The wire where the rotations are applied.
rot_axis (str): The axis around which the rotation is performed.
It can take the value ``X``, ``Y`` or ``Z``. Default is ``Z``.
Raises:
ValueError: If the length of the angles array is not :math:`2^n`, where :math:`n` is the number
of ``control_wires``.
ValueError: If ``rot_axis`` has a value different from ``X``, ``Y`` or ``Z``.
ValueError: If the number of the target wires is not one.
.. seealso:: :class:`~.Select`.
**Example**
.. code-block::
angles = np.array([1.0, 2.0, 3.0, 4.0])
wires = qml.registers({"control": 2, "target": 1})
dev = qml.device("default.qubit", wires=3)
@qml.qnode(dev)
def circuit():
qml.SelectPauliRot(
angles,
control_wires=wires["control"],
target_wire=wires["target"],
rot_axis="Y",
)
return qml.state()
.. code-block:: pycon
>>> print(circuit())
[0.87758256+0.j 0.47942554+0.j 0. +0.j 0. +0.j
0. +0.j 0. +0.j 0. +0.j 0. +0.j]
"""
grad_method = None
ndim_params = (1,)
resource_keys = {"num_wires", "rot_axis"}
def __init__(
self, angles, control_wires, target_wire, rot_axis="Z", id=None
): # pylint: disable=too-many-arguments, too-many-positional-arguments
self.hyperparameters["control_wires"] = qml.wires.Wires(control_wires)
self.hyperparameters["target_wire"] = qml.wires.Wires(target_wire)
self.hyperparameters["rot_axis"] = rot_axis
if qml.math.shape(angles)[-1] != 2 ** len(control_wires):
raise ValueError("Number of angles must be 2^(len(control_wires))")
if rot_axis not in ["X", "Y", "Z"]:
raise ValueError("'rot_axis' can only take the values 'X', 'Y' and 'Z'.")
if len(self.hyperparameters["target_wire"]) != 1:
raise ValueError("Only one target wire can be specified")
all_wires = self.hyperparameters["control_wires"] + self.hyperparameters["target_wire"]
super().__init__(angles, wires=all_wires, id=id)
def _flatten(self):
metadata = tuple((key, value) for key, value in self.hyperparameters.items())
return self.data, metadata
@classmethod
def _unflatten(cls, data, metadata):
hyperparams_dict = dict(metadata)
return cls(*data, **hyperparams_dict)
@property
def resource_params(self) -> dict:
return {"rot_axis": self.hyperparameters["rot_axis"], "num_wires": len(self.wires)}
[docs]
def map_wires(self, wire_map: dict):
"""Map the control and target wires using the provided wire map."""
new_dict = {
key: [wire_map.get(w, w) for w in self.hyperparameters[key]]
for key in ["control_wires", "target_wire"]
}
return SelectPauliRot(
self.parameters[0],
new_dict["control_wires"],
new_dict["target_wire"],
rot_axis=self.hyperparameters["rot_axis"],
)
@classmethod
def _primitive_bind_call(cls, *args, **kwargs):
"""Bind arguments to the primitive operation."""
return cls._primitive.bind(*args, **kwargs)
[docs]
def decomposition(self): # pylint: disable=arguments-differ
"""Return the operator's decomposition using its parameters and hyperparameters."""
return self.compute_decomposition(self.parameters[0], **self.hyperparameters)
[docs]
@staticmethod
def compute_decomposition(
angles, control_wires, target_wire, rot_axis
): # pylint: disable=arguments-differ, too-many-arguments
r"""
Computes the decomposition operations for the given state vector.
Args:
angles (tensor_like): The rotation angles to be applied.
control_wires (Sequence[int]): The control qubits used to select the rotation.
target_wire (Sequence[int]): The wire where the rotations are applied.
rot_axis (str): The axis around the rotation is performed.
It can take the value ``X``, ``Y`` or ``Z``. Default is ``Z``.
Returns:
list: List of decomposition operations.
"""
control_wires = qml.wires.Wires(control_wires)
target_wire = qml.wires.Wires(target_wire)
with qml.queuing.AnnotatedQueue() as q:
decompose_select_pauli_rot(angles, control_wires + target_wire, rot_axis)
if qml.queuing.QueuingManager.recording():
for op in q.queue:
qml.apply(op)
return q.queue
def _select_pauli_rot_resource(num_wires, rot_axis):
return {
qml.RZ: 2 ** (num_wires - 1),
qml.CNOT: 2 ** (num_wires - 1) if num_wires > 1 else 0,
qml.H: 0 if rot_axis == "Z" else 2,
qml.S: 1 if rot_axis == "Y" else 0,
adjoint_resource_rep(qml.S, {}): 1 if rot_axis == "Y" else 0,
}
@register_resources(_select_pauli_rot_resource)
def decompose_select_pauli_rot(angles, wires, rot_axis, **__):
r"""Decomposes the SelectPauliRot"""
if rot_axis == "X":
qml.Hadamard(wires[-1])
elif rot_axis == "Y":
qml.adjoint(qml.S(wires[-1]))
qml.Hadamard(wires[-1])
_apply_uniform_rotation_dagger(qml.RZ, angles, wires[-2::-1], wires[-1])
if rot_axis == "X":
qml.Hadamard(wires[-1])
elif rot_axis == "Y":
qml.Hadamard(wires[-1])
qml.S(wires[-1])
add_decomps(SelectPauliRot, decompose_select_pauli_rot)
_modules/pennylane/templates/subroutines/select_pauli_rot
Download Python script
Download Notebook
View on GitHub