Skip to content

U3 to clifford

SquinU3ToClifford

Bases: RewriteRule


              flowchart TD
              bloqade.squin.rewrite.U3_to_clifford.SquinU3ToClifford[SquinU3ToClifford]

              

              click bloqade.squin.rewrite.U3_to_clifford.SquinU3ToClifford href "" "bloqade.squin.rewrite.U3_to_clifford.SquinU3ToClifford"
            

Rewrite squin U3 statements to clifford when possible.

decompose_U3_gates

decompose_U3_gates(
    node: U3,
) -> list[type[ir.Statement]] | list[None]

Rewrite U3 statements to clifford gates if possible.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/U3_to_clifford.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def decompose_U3_gates(
    self, node: gate.stmts.U3
) -> list[type[ir.Statement]] | list[None]:
    """
    Rewrite U3 statements to clifford gates if possible.
    """
    theta = self.get_constant(node.theta)
    phi = self.get_constant(node.phi)
    lam = self.get_constant(node.lam)

    if theta is None or phi is None or lam is None:
        return []

    # Angles will be in units of turns, we convert to radians
    # to allow for the old logic to work
    theta = theta * math.tau
    phi = phi * math.tau
    lam = lam * math.tau

    # For U3(2*pi*n, phi, lam) = U3(0, 0, lam + phi) which is a Z rotation.
    if np.isclose(np.mod(theta, math.tau), 0):
        lam = lam + phi
        phi = 0.0
    elif np.isclose(np.mod(theta + np.pi, math.tau), 0):
        lam = lam - phi
        phi = 0.0

    theta_half_pi: int | None = self.resolve_angle(theta)
    phi_half_pi: int | None = self.resolve_angle(phi)
    lam_half_pi: int | None = self.resolve_angle(lam)

    if theta_half_pi is None or phi_half_pi is None or lam_half_pi is None:
        return []

    angles_key = (theta_half_pi, phi_half_pi, lam_half_pi)
    if angles_key not in U3_HALF_PI_ANGLE_TO_GATES:
        angles_key = equivalent_u3_para(*angles_key)
        if angles_key not in U3_HALF_PI_ANGLE_TO_GATES:
            return []

    gates_stmts = U3_HALF_PI_ANGLE_TO_GATES.get(angles_key)

    # no consistent gates, then:
    assert (
        gates_stmts is not None
    ), "internal error, U3 gates not found for angles: {}".format(angles_key)

    return gates_stmts

resolve_angle

resolve_angle(angle: float) -> int | None

Normalize the angle to be in the range [0, 2π).

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/U3_to_clifford.py
85
86
87
88
89
90
91
92
93
94
95
96
97
def resolve_angle(self, angle: float) -> int | None:
    """
    Normalize the angle to be in the range [0, 2π).
    """
    # convert to 0.0~1.0, in unit of pi/2
    angle_half_pi = angle / math.pi * 2.0

    mod = angle_half_pi % 1.0
    if not (np.isclose(mod, 0.0) or np.isclose(mod, 1.0)):
        return None

    else:
        return round((angle / math.tau) % 1 * 4) % 4

rewrite_U3

rewrite_U3(node: U3) -> RewriteResult

Rewrite Apply and Broadcast nodes to their clifford equivalent statements.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/U3_to_clifford.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
def rewrite_U3(self, node: gate.stmts.U3) -> RewriteResult:
    """
    Rewrite Apply and Broadcast nodes to their clifford equivalent statements.
    """

    gates = self.decompose_U3_gates(node)

    if len(gates) == 0:
        return RewriteResult()

    # Get rid of the U3 gate altogether if it's identity
    if len(gates) == 1 and gates[0] is None:
        node.delete()
        return RewriteResult(has_done_something=True)

    for gate_stmt in gates:
        if gate_stmt is Sdag:
            new_stmt = gate.stmts.S(adjoint=True, qubits=node.qubits)
        elif gate_stmt is SqrtXdag:
            new_stmt = gate.stmts.SqrtX(adjoint=True, qubits=node.qubits)
        elif gate_stmt is SqrtYdag:
            new_stmt = gate.stmts.SqrtY(adjoint=True, qubits=node.qubits)
        else:
            new_stmt = gate_stmt(qubits=node.qubits)
        new_stmt.insert_before(node)

    node.delete()

    return RewriteResult(has_done_something=True)

equivalent_u3_para

equivalent_u3_para(
    theta_half_pi: int, phi_half_pi: int, lam_half_pi: int
) -> tuple[int, int, int]
  1. Assume all three angles are in the range [0, 4].
  2. U3(theta, phi, lam) = -U3(2pi-theta, phi+pi, lam+pi).
Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/U3_to_clifford.py
57
58
59
60
61
62
63
64
def equivalent_u3_para(
    theta_half_pi: int, phi_half_pi: int, lam_half_pi: int
) -> tuple[int, int, int]:
    """
    1. Assume all three angles are in the range [0, 4].
    2. U3(theta, phi, lam) = -U3(2pi-theta, phi+pi, lam+pi).
    """
    return ((4 - theta_half_pi) % 4, (phi_half_pi + 2) % 4, (lam_half_pi + 2) % 4)