qml.transforms.merge_rotations¶
- merge_rotations(tape, atol=1e-08, include_gates=None)[source]¶
Quantum transform to combine rotation gates of the same type that act sequentially.
If the combination of two rotation produces an angle that is close to 0, neither gate will be applied.
- Parameters
tape (QNode or QuantumTape or Callable) – A quantum circuit.
atol (float) – After fusion of gates, if the fused angle \(\theta\) is such that \(|\theta|\leq \text{atol}\), no rotation gate will be applied.
include_gates (None or list[str]) – A list of specific operations to merge. If set to
None
(default), all operations in the ~.pennylane.ops.qubit.attributes.composable_rotations attribute will be merged. Otherwise, only the operations whose names match those in the list will undergo merging.
- Returns
The transformed circuit as described in
qml.transform
.- Return type
qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]
Example
>>> dev = qml.device('default.qubit', wires=3)
You can apply the transform directly on
QNode
@merge_rotations @qml.qnode(device=dev) def circuit(x, y, z): qml.RX(x, wires=0) qml.RX(y, wires=0) qml.CNOT(wires=[1, 2]) qml.RY(y, wires=1) qml.Hadamard(wires=2) qml.CRZ(z, wires=[2, 0]) qml.RY(-y, wires=1) return qml.expval(qml.Z(0))
>>> circuit(0.1, 0.2, 0.3) 0.9553364891256055
Details on merging ``Rot`` gates
When merging two
Rot
gates, there are a number of details to consider:First, the output angles are not always defined uniquely, because Euler angles are not unique for some rotations.
merge_rotations
makes a particular choice in this case.Second,
merge_rotations
is not differentiable everywhere when used onRot
. It has singularities for specific rotation angles where the derivative will be NaN.Finally, this function can be numerically unstable near singular points. It is therefore recommended to use it with 64-bit floating point precision angles.
For a mathematical derivation of the fusion of two
Rot
gates, see the documentation ofsingle_qubit_fusion()
.Usage Details
You can also apply
merge_rotations
to a quantum function.def qfunc(x, y, z): qml.RX(x, wires=0) qml.RX(y, wires=0) qml.CNOT(wires=[1, 2]) qml.RY(y, wires=1) qml.Hadamard(wires=2) qml.CRZ(z, wires=[2, 0]) qml.RY(-y, wires=1) return qml.expval(qml.Z(0))
The circuit before optimization:
>>> qnode = qml.QNode(qfunc, dev) >>> print(qml.draw(qnode)(1, 2, 3)) 0: ──RX(1.00)──RX(2.00)─╭RZ(3.00)────────────┤ <Z> 1: ─╭●─────────RY(2.00)─│──────────RY(-2.00)─┤ 2: ─╰X─────────H────────╰●───────────────────┤
By inspection, we can combine the two
RX
rotations on the first qubit. On the second qubit, we have a cumulative angle of 0, and the gates will cancel.>>> optimized_qfunc = merge_rotations()(qfunc) >>> optimized_qnode = qml.QNode(optimized_qfunc, dev) >>> print(qml.draw(optimized_qnode)(1, 2, 3)) 0: ──RX(3.00)────╭RZ(3.00)─┤ <Z> 1: ─╭●───────────│─────────┤ 2: ─╰X─────────H─╰●────────┤
It is also possible to explicitly specify which rotations
merge_rotations
should merge using theinclude_gates
argument. For example, if in the above circuit we wanted only to merge the “RX” gates, we could do so as follows:>>> optimized_qfunc = merge_rotations(include_gates=["RX"])(qfunc) >>> optimized_qnode = qml.QNode(optimized_qfunc, dev) >>> print(qml.draw(optimized_qnode)(1, 2, 3)) 0: ──RX(3.00)───────────╭RZ(3.00)────────────┤ <Z> 1: ─╭●─────────RY(2.00)─│──────────RY(-2.00)─┤ 2: ─╰X─────────H────────╰●───────────────────┤