Source code for pennylane.labs.transforms.rot_to_phase_gradient
# Copyright 2026 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 ``rot_to_phase_gradient`` transform.
"""
# pylint: disable=too-many-branches
import numpy as np
import pennylane as qp
from pennylane.operation import Operator
from pennylane.queuing import QueuingManager
from pennylane.transforms.rz_phase_gradient import _rz_phase_gradient
from pennylane.wires import Wires
[docs]
def ladder(wires):
"""ladder operator"""
if len(wires) == 1:
return qp.I(wires)
return qp.prod(*[qp.CNOT(wires) for wires in zip(wires[1:], wires[:-1])])
# pylint: disable=too-many-arguments
def _select_pauli_rot_phase_gradient(
phis: np.ndarray,
rot_axis: str,
control_wires: Wires,
target_wire: Wires,
angle_wires: Wires,
phase_grad_wires: Wires,
work_wires: Wires,
) -> Operator:
"""Function that transforms the SelectPauliRot gate to the phase gradient circuit
The precision is implicitly defined by the length of ``angle_wires``
"""
precision = len(angle_wires)
binary_int = qp.math.binary_decimals(phis, precision, unit=4 * np.pi)
ops = [
qp.QROM(
binary_int, control_wires, angle_wires, work_wires=work_wires[: len(control_wires) - 1]
)
] + [qp.ctrl(qp.X(wire), control=target_wire, control_values=[0]) for wire in phase_grad_wires]
# The uncomputation does not need any adjoints because both QROM and C(X) are self-adjoint.
adj_ops = ops[::-1]
pg_op = qp.change_op_basis(
qp.prod(*ops[::-1]),
qp.SemiAdder(angle_wires, phase_grad_wires, work_wires=work_wires[: len(angle_wires) - 1]),
qp.prod(*adj_ops[::-1]),
)
match rot_axis:
case "X":
comp = uncomp = qp.Hadamard(target_wire)
pg_op = qp.change_op_basis(comp, pg_op, uncomp)
case "Y":
comp = qp.Hadamard(target_wire) @ qp.adjoint(qp.S(target_wire))
uncomp = qp.S(target_wire) @ qp.Hadamard(target_wire)
pg_op = qp.change_op_basis(comp, pg_op, uncomp)
return pg_op
def _pauli_rot_phase_gradient(op, **other_wires):
wires = op.wires
phi = op.parameters[0]
if isinstance(op, (qp.IsingXX, qp.IsingYY, qp.IsingZZ)):
with QueuingManager.stop_recording():
pauli_word = op.name[-2:]
op = qp.PauliRot(phi, pauli_word=pauli_word, wires=wires)
# collect diagonalizing gates of each wire
# this turns any rotation to MultiRZ
diagonalizing_gates = []
for sub_op in op.decomposition():
if isinstance(sub_op, qp.MultiRZ):
break
diagonalizing_gates.append(sub_op)
diagonalizing_gate = ladder(wires) @ qp.prod(*diagonalizing_gates[::-1])
diagonalizing_gate_inv = qp.prod(*diagonalizing_gates) @ ladder(wires)
pg_op = _rz_phase_gradient(phi, wires[:1], **other_wires)
new_op = qp.change_op_basis(diagonalizing_gate, pg_op, diagonalizing_gate_inv)
return new_op, phi / 2 # op to be appended, global phase
_modules/pennylane/labs/transforms/rot_to_phase_gradient
Download Python script
Download Notebook
View on GitHub