qml.transforms.single_qubit_fusion¶
- single_qubit_fusion(tape, atol=1e-08, exclude_gates=None)[source]¶
Quantum function transform to fuse together groups of single-qubit operations into a general single-qubit unitary operation (
Rot
).Fusion is performed only between gates that implement the property
single_qubit_rot_angles
. Any sequence of two or more single-qubit gates (on the same qubit) with that property defined will be fused into oneRot
.- Parameters
tape (QNode or QuantumTape or Callable) – A quantum circuit.
atol (float) – An absolute tolerance for which to apply a rotation after fusion. After fusion of gates, if the fused angles θ are such that |θ|≤atol, no rotation gate will be applied.
exclude_gates (None or list[str]) – A list of gates that should be excluded from full fusion. If set to
None
, all single-qubit gates that can be fused will be fused.
- Returns
The transformed circuit as described in
qml.transform
.- Return type
qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], Callable]
Example
>>> dev = qml.device('default.qubit', wires=1)
You can apply the transform directly on
QNode
:@qml.transforms.single_qubit_fusion @qml.qnode(device=dev) def qfunc(r1, r2): qml.Hadamard(wires=0) qml.Rot(*r1, wires=0) qml.Rot(*r2, wires=0) qml.RZ(r1[0], wires=0) qml.RZ(r2[0], wires=0) return qml.expval(qml.X(0))
The single qubit gates are fused before execution.
Note
The fused angles between two sets of rotation angles are not always defined uniquely because Euler angles are not unique for some rotations.
single_qubit_fusion
makes a particular choice in this case.Note
The order of the gates resulting from the fusion may be different depending on whether program capture is enabled or not. This only impacts the order of operations that do not share any wires, so the correctness of the circuit is not affected.
Warning
This function is not differentiable everywhere. It has singularities for specific input rotation angles, where the derivative will be NaN.
Warning
This function is numerically unstable at its singular points. It is recommended to use it with 64-bit floating point precision.
Usage Details
Consider the following quantum function.
def qfunc(r1, r2): qml.Hadamard(wires=0) qml.Rot(*r1, wires=0) qml.Rot(*r2, wires=0) qml.RZ(r1[0], wires=0) qml.RZ(r2[0], wires=0) return qml.expval(qml.X(0))
The circuit before optimization:
>>> qnode = qml.QNode(qfunc, dev) >>> print(qml.draw(qnode)([0.1, 0.2, 0.3], [0.4, 0.5, 0.6])) 0: ──H──Rot(0.1, 0.2, 0.3)──Rot(0.4, 0.5, 0.6)──RZ(0.1)──RZ(0.4)──┤ ⟨X⟩
Full single-qubit gate fusion allows us to collapse this entire sequence into a single
qml.Rot
rotation gate.>>> optimized_qfunc = qml.transforms.single_qubit_fusion(qfunc) >>> optimized_qnode = qml.QNode(optimized_qfunc, dev) >>> print(qml.draw(optimized_qnode)([0.1, 0.2, 0.3], [0.4, 0.5, 0.6])) 0: ──Rot(3.57, 2.09, 2.05)──┤ ⟨X⟩
Derivation
The matrix for an individual rotation is given by
R(ϕj,θj,ωj)=[e−i(ϕj+ωj)/2cos(θj/2)−ei(ϕj−ωj)/2sin(θj/2)e−i(ϕj−ωj)/2sin(θj/2)ei(ϕj+ωj)/2cos(θj/2)]=[e−iαjcj−eiβjsje−iβjsjeiαjcj],where we introduced abbreviations αj,βj=ϕj±ωj2, cj=cos(θj/2) and sj=sin(θj/2) for notational brevity. The upper left entry of the matrix product R(ϕ2,θ2,ω2)R(ϕ1,θ1,ω1) reads
x=e−i(α2+α1)c2c1−ei(β2−β1)s2s1and should equal e−iαfcf for the fused rotation angles. This means that we can obtain θf from the magnitude of the matrix product entry above, choosing cf=cos(θf/2) to be non-negative:
cf=|x|=|e−i(α2+α1)c2c1−ei(β2−β1)s2s1|=√c21c22+s21s22−2c1c2s1s2cos(ω1+ϕ2).Now we again make a choice and pick θf to be non-negative:
θf=2arccos(|x|).We can also extract the angle combination αf from x via arg(x), which can be readily computed with arctan:
αf=−arctan(−c1c2sin(α1+α2)−s1s2sin(β2−β1)c1c2cos(α1+α2)−s1s2cos(β2−β1)).We can use the standard numerical function
arctan2
, which computes arctan(x1/x2) from x1 and x2 while handling special points suitably, to obtain the argument of the underlying complex number x2+x1i.Finally, to obtain βf, we need a second element of the matrix product from above. We compute the lower-left entry to be
y=e−i(β2+α1)s2c1+ei(α2−β1)c2s1,which should equal e−iβfsf. From this, we can compute
βf=−arctan(−c1s2sin(α1+β2)+s1c2sin(α2−β1)c1s2cos(α1+β2)+s1c2cos(α2−β1)).From this, we may extract
ϕf=αf+βfωf=αf−βfand are done.
Special cases:
There are a number of special cases for which we can skip the computation above and can combine rotation angles directly.
If ω1=ϕ2=0, we can simply merge the
RY
rotation angles θj and obtain (ϕ1,θ1+θ2,ω2).If θj=0, we can merge the two
RZ
rotations of the sameRot
and obtain (ϕ1+ω1+ϕ2,θ2,ω2) or (ϕ1,θ1,ω1+ϕ2+ω2). If bothRY
angles vanish we get (ϕ1+ω1+ϕ2+ω2,0,0).
Note that this optimization is not performed for differentiable input parameters, in order to maintain differentiability.
Mathematical properties:
All functions above are well-defined on the domain we are using them on, if we handle arctan via standard numerical implementations such as
np.arctan2
. Based on the choices we made in the derivation above, the fused angles will lie in the intervalsϕf,ωf∈[−π,π],θf∈[0,π].Close to the boundaries of these intervals,
single_qubit_fusion
exhibits discontinuities, depending on the combination of input angles. These discontinuities also lead to singular (non-differentiable) points as discussed below.Differentiability:
The function derived above is differentiable almost everywhere. In particular, there are two problematic scenarios at which the derivative is not defined. First, the square root is not differentiable at 0, making all input angles with |x|=0 singular. Second, arccos is not differentiable at 1, making all input angles with |x|=1 singular.