qml.transforms.unitary_to_rot¶
- unitary_to_rot(tape)[source]¶
Decompose all single-qubit and two-qubit
QubitUnitaryoperations to parametrized single-qubit operations and CNOTs.Single-qubit gates will be converted to a sequence of Y and Z rotations in the form \(RZ(\omega) RY(\theta) RZ(\phi)\) that implements the original operation up to a global phase. Two-qubit gates will be decomposed according to the
pennylane.transforms.two_qubit_decomposition()function.Warning
This transform is not fully differentiable for 2-qubit
QubitUnitaryoperations. See usage details below.- Parameters:
tape (QNode or QuantumTape or Callable) – A quantum circuit.
- Returns:
The transformed circuit as described in
qml.transform.- Return type:
qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]
Example
Suppose we would like to apply the following unitary operation:
U = np.array([ [-0.17111489+0.58564875j, -0.69352236-0.38309524j], [ 0.25053735+0.75164238j, 0.60700543-0.06171855j] ])
The
unitary_to_rottransform enables us to decompose such numerical operations while preserving differentiability.@qml.transforms.unitary_to_rot @qml.qnode(qml.device("default.qubit")) def circuit(): qml.QubitUnitary(U, wires=0) return qml.expval(qml.Z(0))
The original circuit is:
>>> print(qml.draw(circuit, level=0)()) 0: ──U(M0)─┤ <Z> M0 = [[-0.171...+0.5856...j -0.693...-0.383...j] [ 0.250...+0.751...j 0.607...-0.061...j]]
We can use the transform to decompose the gate:
>>> print(qml.draw(circuit, level=1)()) 0: ──RZ(11.22)──RY(1.83)──RZ(11.96)─┤ <Z>
Usage Details
This decomposition is not fully differentiable. We can differentiate with respect to input QNode parameters when they are not used to explicitly construct a \(4 \times 4\) unitary matrix being decomposed. So for example, the following will work:
import scipy import pennylane.numpy as pnp U = scipy.stats.unitary_group.rvs(4, random_state=12345) @qml.transforms.unitary_to_rot @qml.qnode(qml.device("default.qubit")) def circuit(angles): qml.QubitUnitary(U, wires=["a", "b"]) qml.RX(angles[0], wires="a") qml.RY(angles[1], wires="b") qml.CNOT(wires=["b", "a"]) return qml.expval(qml.Z("a"))
>>> g = qml.grad(circuit) >>> params = pnp.array([0.2, 0.3], requires_grad=True) >>> g(params) array([ 0.342..., -0.077...])
However, the following example will not be differentiable:
@qml.transforms.unitary_to_rot @qml.qnode(qml.device("default.qubit")) def circuit(angles): z = angles[0] x = angles[1] Z_mat = pnp.array([[pnp.exp(-1j * z / 2), 0.0], [0.0, pnp.exp(1j * z / 2)]]) c = pnp.cos(x / 2) s = pnp.sin(x / 2) * 1j X_mat = pnp.array([[c, -s], [-s, c]]) U = pnp.kron(Z_mat, X_mat) qml.Hadamard(wires="a") # U depends on the input parameters qml.QubitUnitary(U, wires=["a", "b"]) qml.CNOT(wires=["b", "a"]) return qml.expval(qml.X("a"))
>>> g = qml.grad(circuit) >>> params = pnp.array([0.2, 0.3], requires_grad=True) >>> g(params) array([nan, nan])