Skip to content

U3 to clifford

SquinU3ToClifford

Bases: RewriteRule

Rewrite squin U3 and rotation gate 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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
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
113
114
115
116
117
118
119
120
121
122
123
124
125
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
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)

rewrite_rotation

rewrite_rotation(
    node: RotationGate,
    clifford_map: dict[int, type[Statement] | None],
) -> RewriteResult

Rewrite a rotation gate (Rx, Ry, Rz) to its Clifford equivalent if possible.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/U3_to_clifford.py
127
128
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
def rewrite_rotation(
    self,
    node: gate.stmts.RotationGate,
    clifford_map: dict[int, type[ir.Statement] | None],
) -> RewriteResult:
    """
    Rewrite a rotation gate (Rx, Ry, Rz) to its Clifford equivalent if possible.
    """
    angle = self.get_constant(node.angle)
    if angle is None:
        return RewriteResult()

    angle_half_pi = self.resolve_angle(angle * math.tau)
    if angle_half_pi is None:
        return RewriteResult()

    gate_type = clifford_map.get(angle_half_pi)

    if gate_type is None:
        node.delete()
        return RewriteResult(has_done_something=True)

    if gate_type is Sdag:
        new_stmt = gate.stmts.S(adjoint=True, qubits=node.qubits)
    elif gate_type is SqrtXdag:
        new_stmt = gate.stmts.SqrtX(adjoint=True, qubits=node.qubits)
    elif gate_type is SqrtYdag:
        new_stmt = gate.stmts.SqrtY(adjoint=True, qubits=node.qubits)
    else:
        new_stmt = gate_type(qubits=node.qubits)

    node.replace_by(new_stmt)

    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
79
80
81
82
83
84
85
86
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)