qml.labs.transforms.select_pauli_rot_phase_gradient

select_pauli_rot_phase_gradient(tape, angle_wires, phase_grad_wires, work_wires)[source]

Quantum function transform to decompose all instances of SelectPauliRot gates into additions using a phase gradient resource state.

For this routine to work, the provided phase_grad_wires need to hold the phase gradient state \(|\nabla_Z\rangle = \frac{1}{\sqrt{2^n}} \sum_{m=0}^{2^n-1} e^{-2 \pi i \frac{m}{2^n}} |m\rangle\). Because this state is not modified and can be re-used at a later stage, the transform does not prepare it but rather assumes it has been prepared on those wires at an earlier stage. Look at the example below to see how this state can be prepared.

../../../_images/multiplexer_QROM.png

Note that this operator contains SemiAdder that typically uses additional work_wires for the semi-in-place addition \(\text{SemiAdder}|x\rangle_\text{ang} |y\rangle_\text{phg} = |x\rangle_\text{ang} |x + y\rangle_\text{phg}\).

Parameters:
  • tape (QNode or QuantumTape or Callable) – A quantum circuit containing SelectPauliRot operators.

  • angle_wires (Wires) – The qubits that conditionally load the angle \(\phi\) of the SelectPauliRot gate in binary as a multiple of \(2\pi\). The length of the angle_wires , i.e. \(b\), implicitly determines the precision with which the angle is represented. E.g., \((1 \cdot 2^{-1} + 0 \cdot 2^{-2} + 1 \cdot 2^{-3}) 2\pi\) is represented by three bits as 101.

  • phase_grad_wires (Wires) – Qubits with the catalytic phase gradient state prepared on them. Needs to be at least \(b\) wires and will only use the first \(b\).

  • work_wires (Wires) – Additional work wires to realize the SemiAdder and QROM. Needs to be at least \(b-1\) wires.

Returns:

The transformed circuit as described in qml.transform.

Return type:

qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]

Example

from pennylane.labs.transforms import select_pauli_rot_phase_gradient
from functools import partial
import numpy as np

precision = 4
wire = "targ"
angle_wires = [f"ang_{i}" for i in range(precision)]
phase_grad_wires = [f"phg_{i}" for i in range(precision)]
work_wires = [f"work_{i}" for i in range(precision - 1)]

def phase_gradient(wires):
    # prepare phase gradient state
    for i, w in enumerate(wires):
        qml.H(w)
        qml.PhaseShift(-np.pi / 2**i, w)

@partial(
    select_pauli_rot_phase_gradient,
    angle_wires=angle_wires,
    phase_grad_wires=phase_grad_wires,
    work_wires=work_wires,
)
@qml.qnode(qml.device("default.qubit"))
def select_pauli_rot_circ(phis, control_wires, target_wire):
    phase_gradient(phase_grad_wires)  # prepare phase gradient state

    for wire in control_wires:
        qml.Hadamard(wire)

    qml.SelectPauliRot(phis, control_wires, target_wire, rot_axis="X")

    return qml.probs(target_wire)

phis = [
    (1 / 2 + 1 / 4 + 1 / 8) * 2 * np.pi,
    (1 / 2 + 1 / 4 + 0 / 8) * 2 * np.pi,
    (1 / 2 + 0 / 4 + 1 / 8) * 2 * np.pi,
    (0 / 2 + 1 / 4 + 1 / 8) * 2 * np.pi,
]
>>> print(select_pauli_rot_circ(phis, control_wires=[0, 1], target_wire=wire))
[0.41161165 0.58838835]