Skip to content

mckay_decomposer

McKayDecomposer

Bases: Decomposer

Source code in opensquirrel\decomposer\mckay_decomposer.py
class McKayDecomposer(Decomposer):
    def decompose(self, g: Gate) -> list[Gate]:
        """Return the McKay decomposition of a 1-qubit gate as a list of gates.
                gate   ---->    Rz.Rx(pi/2).Rz.Rx(pi/2).Rz

        The global phase is deemed _irrelevant_, therefore a simulator backend might produce different output.
        The results should be equivalent modulo global phase.
        Notice that, if the gate is Rz or X90, it will not be decomposed further, since they are natively used
        in the McKay decomposition.

        Relevant literature: https://arxiv.org/abs/1612.00858
        """
        if not isinstance(g, BlochSphereRotation) or g.name == "Rz" or g.name == "X90":
            return [g]

        if abs(g.angle) < ATOL:
            return []

        if g.axis[0] == 0 and g.axis[1] == 0:
            rz_angle = float(g.angle * g.axis[2])
            return [Rz(g.qubit, Float(rz_angle))]

        zxz_decomposition = ZXZDecomposer().decompose(g)
        zxz_angle = 0.0
        if len(zxz_decomposition) >= 2 and isinstance(zxz_decomposition[1], BlochSphereRotation):
            zxz_angle = zxz_decomposition[1].angle

        if abs(zxz_angle - pi / 2) < ATOL:
            zxz_decomposition[1] = X90(g.qubit)
            return zxz_decomposition

        # McKay decomposition
        za_mod = sqrt(cos(g.angle / 2) ** 2 + (g.axis[2] * sin(g.angle / 2)) ** 2)
        zb_mod = abs(sin(g.angle / 2)) * sqrt(g.axis[0] ** 2 + g.axis[1] ** 2)

        theta = pi - 2 * atan2(zb_mod, za_mod)

        alpha = atan2(-sin(g.angle / 2) * g.axis[2], cos(g.angle / 2))
        beta = atan2(-sin(g.angle / 2) * g.axis[0], -sin(g.angle / 2) * g.axis[1])

        lam = beta - alpha
        phi = -beta - alpha - pi

        lam = normalize_angle(lam)
        phi = normalize_angle(phi)
        theta = normalize_angle(theta)

        decomposed_g: list[Gate] = []

        if abs(theta) < ATOL and lam == phi:
            decomposed_g.append(X90(g.qubit))
            decomposed_g.append(X90(g.qubit))
            return decomposed_g

        if abs(lam) > ATOL:
            decomposed_g.append(Rz(g.qubit, Float(lam)))

        decomposed_g.append(X90(g.qubit))

        if abs(theta) > ATOL:
            decomposed_g.append(Rz(g.qubit, Float(theta)))

        decomposed_g.append(X90(g.qubit))

        if abs(phi) > ATOL:
            decomposed_g.append(Rz(g.qubit, Float(phi)))

        return decomposed_g

decompose(g)

Return the McKay decomposition of a 1-qubit gate as a list of gates. gate ----> Rz.Rx(pi/2).Rz.Rx(pi/2).Rz

The global phase is deemed irrelevant, therefore a simulator backend might produce different output. The results should be equivalent modulo global phase. Notice that, if the gate is Rz or X90, it will not be decomposed further, since they are natively used in the McKay decomposition.

Relevant literature: https://arxiv.org/abs/1612.00858

Source code in opensquirrel\decomposer\mckay_decomposer.py
def decompose(self, g: Gate) -> list[Gate]:
    """Return the McKay decomposition of a 1-qubit gate as a list of gates.
            gate   ---->    Rz.Rx(pi/2).Rz.Rx(pi/2).Rz

    The global phase is deemed _irrelevant_, therefore a simulator backend might produce different output.
    The results should be equivalent modulo global phase.
    Notice that, if the gate is Rz or X90, it will not be decomposed further, since they are natively used
    in the McKay decomposition.

    Relevant literature: https://arxiv.org/abs/1612.00858
    """
    if not isinstance(g, BlochSphereRotation) or g.name == "Rz" or g.name == "X90":
        return [g]

    if abs(g.angle) < ATOL:
        return []

    if g.axis[0] == 0 and g.axis[1] == 0:
        rz_angle = float(g.angle * g.axis[2])
        return [Rz(g.qubit, Float(rz_angle))]

    zxz_decomposition = ZXZDecomposer().decompose(g)
    zxz_angle = 0.0
    if len(zxz_decomposition) >= 2 and isinstance(zxz_decomposition[1], BlochSphereRotation):
        zxz_angle = zxz_decomposition[1].angle

    if abs(zxz_angle - pi / 2) < ATOL:
        zxz_decomposition[1] = X90(g.qubit)
        return zxz_decomposition

    # McKay decomposition
    za_mod = sqrt(cos(g.angle / 2) ** 2 + (g.axis[2] * sin(g.angle / 2)) ** 2)
    zb_mod = abs(sin(g.angle / 2)) * sqrt(g.axis[0] ** 2 + g.axis[1] ** 2)

    theta = pi - 2 * atan2(zb_mod, za_mod)

    alpha = atan2(-sin(g.angle / 2) * g.axis[2], cos(g.angle / 2))
    beta = atan2(-sin(g.angle / 2) * g.axis[0], -sin(g.angle / 2) * g.axis[1])

    lam = beta - alpha
    phi = -beta - alpha - pi

    lam = normalize_angle(lam)
    phi = normalize_angle(phi)
    theta = normalize_angle(theta)

    decomposed_g: list[Gate] = []

    if abs(theta) < ATOL and lam == phi:
        decomposed_g.append(X90(g.qubit))
        decomposed_g.append(X90(g.qubit))
        return decomposed_g

    if abs(lam) > ATOL:
        decomposed_g.append(Rz(g.qubit, Float(lam)))

    decomposed_g.append(X90(g.qubit))

    if abs(theta) > ATOL:
        decomposed_g.append(Rz(g.qubit, Float(theta)))

    decomposed_g.append(X90(g.qubit))

    if abs(phi) > ATOL:
        decomposed_g.append(Rz(g.qubit, Float(phi)))

    return decomposed_g