Loading [MathJax]/jax/output/HTML-CSS/jax.js

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 one Rot.

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.

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⟩

The matrix for an individual rotation is given by

R(ϕj,θj,ωj)=[ei(ϕj+ωj)/2cos(θj/2)ei(ϕjωj)/2sin(θj/2)ei(ϕjωj)/2sin(θj/2)ei(ϕj+ωj)/2cos(θj/2)]=[eiαjcjeiβjsjeiβ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=ei(α2+α1)c2c1ei(β2β1)s2s1

and should equal eiα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|=|ei(α2+α1)c2c1ei(β2β1)s2s1|=c21c22+s21s222c1c2s1s2cos(ω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=ei(β2+α1)s2c1+ei(α2β1)c2s1,

which should equal eiβ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βf

and 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.

  1. If ω1=ϕ2=0, we can simply merge the RY rotation angles θj and obtain (ϕ1,θ1+θ2,ω2).

  2. If θj=0, we can merge the two RZ rotations of the same Rot and obtain (ϕ1+ω1+ϕ2,θ2,ω2) or (ϕ1,θ1,ω1+ϕ2+ω2). If both RY 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.