bloqade-circuit 0.7.12__py3-none-any.whl → 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bloqade-circuit might be problematic. Click here for more details.
- bloqade/analysis/address/__init__.py +8 -4
- bloqade/analysis/address/analysis.py +119 -29
- bloqade/analysis/address/impls.py +290 -87
- bloqade/analysis/address/lattice.py +209 -24
- bloqade/analysis/fidelity/analysis.py +2 -2
- bloqade/analysis/measure_id/impls.py +3 -27
- bloqade/cirq_utils/__init__.py +3 -1
- bloqade/cirq_utils/emit/__init__.py +3 -0
- bloqade/cirq_utils/emit/base.py +243 -0
- bloqade/cirq_utils/emit/gate.py +104 -0
- bloqade/cirq_utils/emit/noise.py +90 -0
- bloqade/cirq_utils/emit/qubit.py +35 -0
- bloqade/cirq_utils/lowering.py +664 -0
- bloqade/native/__init__.py +0 -1
- bloqade/native/_prelude.py +3 -3
- bloqade/native/dialects/gate/__init__.py +2 -0
- bloqade/native/dialects/gate/_dialect.py +3 -0
- bloqade/native/dialects/{gates → gate}/_interface.py +5 -5
- bloqade/native/dialects/{gates → gate}/stmts.py +5 -5
- bloqade/native/stdlib/broadcast.py +19 -19
- bloqade/native/stdlib/simple.py +14 -13
- bloqade/native/upstream/__init__.py +5 -0
- bloqade/native/upstream/squin2native.py +136 -0
- bloqade/pyqrack/__init__.py +1 -2
- bloqade/pyqrack/device.py +6 -17
- bloqade/pyqrack/native.py +17 -17
- bloqade/pyqrack/reg.py +1 -6
- bloqade/pyqrack/squin/gate/__init__.py +1 -0
- bloqade/pyqrack/squin/gate/gate.py +136 -0
- bloqade/pyqrack/squin/noise/native.py +120 -54
- bloqade/pyqrack/squin/qubit.py +25 -41
- bloqade/pyqrack/target.py +2 -2
- bloqade/qasm2/dialects/core/address.py +21 -12
- bloqade/qasm2/dialects/noise/fidelity.py +2 -6
- bloqade/qasm2/dialects/noise/model.py +2 -1
- bloqade/qasm2/passes/parallel.py +3 -1
- bloqade/qasm2/rewrite/__init__.py +0 -1
- bloqade/qasm2/rewrite/noise/heuristic_noise.py +7 -17
- bloqade/qasm2/rewrite/parallel_to_glob.py +28 -15
- bloqade/qasm2/rewrite/parallel_to_uop.py +2 -8
- bloqade/qubit/__init__.py +12 -0
- bloqade/qubit/_dialect.py +3 -0
- bloqade/qubit/_interface.py +49 -0
- bloqade/qubit/_prelude.py +45 -0
- bloqade/qubit/analysis/__init__.py +1 -0
- bloqade/qubit/analysis/address_impl.py +40 -0
- bloqade/qubit/stdlib/__init__.py +2 -0
- bloqade/qubit/stdlib/_new.py +34 -0
- bloqade/qubit/stdlib/broadcast.py +62 -0
- bloqade/qubit/stdlib/simple.py +59 -0
- bloqade/qubit/stmts.py +60 -0
- bloqade/rewrite/passes/aggressive_unroll.py +2 -1
- bloqade/squin/__init__.py +44 -17
- bloqade/squin/analysis/__init__.py +0 -1
- bloqade/squin/analysis/schedule.py +2 -2
- bloqade/squin/gate/__init__.py +2 -0
- bloqade/squin/gate/_dialect.py +3 -0
- bloqade/squin/gate/_interface.py +98 -0
- bloqade/squin/gate/stmts.py +119 -0
- bloqade/squin/groups.py +4 -21
- bloqade/squin/noise/__init__.py +1 -9
- bloqade/squin/noise/_dialect.py +1 -1
- bloqade/squin/noise/_interface.py +45 -0
- bloqade/squin/noise/stmts.py +65 -29
- bloqade/squin/rewrite/U3_to_clifford.py +70 -51
- bloqade/squin/rewrite/__init__.py +0 -2
- bloqade/squin/rewrite/remove_dangling_qubits.py +2 -2
- bloqade/squin/rewrite/wrap_analysis.py +4 -35
- bloqade/squin/stdlib/broadcast/__init__.py +34 -0
- bloqade/squin/stdlib/broadcast/_qubit.py +4 -0
- bloqade/squin/stdlib/broadcast/gate.py +260 -0
- bloqade/squin/stdlib/broadcast/noise.py +144 -0
- bloqade/squin/stdlib/simple/__init__.py +33 -0
- bloqade/squin/stdlib/simple/gate.py +242 -0
- bloqade/squin/stdlib/simple/noise.py +126 -0
- bloqade/stim/__init__.py +1 -0
- bloqade/stim/_wrappers.py +6 -0
- bloqade/stim/dialects/noise/emit.py +6 -1
- bloqade/stim/dialects/noise/stmts.py +5 -3
- bloqade/stim/emit/stim_str.py +2 -0
- bloqade/stim/parse/lowering.py +12 -17
- bloqade/stim/passes/__init__.py +0 -1
- bloqade/stim/passes/flatten.py +26 -0
- bloqade/stim/passes/simplify_ifs.py +6 -1
- bloqade/stim/passes/squin_to_stim.py +4 -70
- bloqade/stim/rewrite/__init__.py +0 -4
- bloqade/stim/rewrite/ifs_to_stim.py +23 -29
- bloqade/stim/rewrite/qubit_to_stim.py +90 -41
- bloqade/stim/rewrite/squin_measure.py +9 -18
- bloqade/stim/rewrite/squin_noise.py +132 -108
- bloqade/stim/rewrite/util.py +5 -204
- bloqade/types.py +10 -0
- {bloqade_circuit-0.7.12.dist-info → bloqade_circuit-0.8.0.dist-info}/METADATA +2 -2
- {bloqade_circuit-0.7.12.dist-info → bloqade_circuit-0.8.0.dist-info}/RECORD +96 -100
- bloqade/native/dialects/gates/__init__.py +0 -3
- bloqade/native/dialects/gates/_dialect.py +0 -3
- bloqade/pyqrack/squin/op.py +0 -180
- bloqade/pyqrack/squin/runtime.py +0 -543
- bloqade/pyqrack/squin/wire.py +0 -51
- bloqade/squin/_typeinfer.py +0 -20
- bloqade/squin/analysis/address_impl.py +0 -71
- bloqade/squin/analysis/nsites/__init__.py +0 -9
- bloqade/squin/analysis/nsites/analysis.py +0 -50
- bloqade/squin/analysis/nsites/impls.py +0 -99
- bloqade/squin/analysis/nsites/lattice.py +0 -49
- bloqade/squin/cirq/__init__.py +0 -306
- bloqade/squin/cirq/emit/emit_circuit.py +0 -129
- bloqade/squin/cirq/emit/noise.py +0 -49
- bloqade/squin/cirq/emit/op.py +0 -176
- bloqade/squin/cirq/emit/qubit.py +0 -58
- bloqade/squin/cirq/emit/runtime.py +0 -242
- bloqade/squin/cirq/lowering.py +0 -439
- bloqade/squin/lowering.py +0 -80
- bloqade/squin/noise/_wrapper.py +0 -36
- bloqade/squin/noise/rewrite.py +0 -129
- bloqade/squin/op/__init__.py +0 -41
- bloqade/squin/op/_dialect.py +0 -3
- bloqade/squin/op/_wrapper.py +0 -121
- bloqade/squin/op/number.py +0 -5
- bloqade/squin/op/rewrite.py +0 -46
- bloqade/squin/op/stdlib.py +0 -62
- bloqade/squin/op/stmts.py +0 -300
- bloqade/squin/op/traits.py +0 -43
- bloqade/squin/op/types.py +0 -128
- bloqade/squin/parallel.py +0 -200
- bloqade/squin/qubit.py +0 -194
- bloqade/squin/rewrite/canonicalize.py +0 -60
- bloqade/squin/rewrite/desugar.py +0 -102
- bloqade/squin/stdlib/channel.py +0 -86
- bloqade/squin/stdlib/gate.py +0 -201
- bloqade/squin/types.py +0 -8
- bloqade/squin/wire.py +0 -201
- bloqade/stim/rewrite/wire_identity_elimination.py +0 -24
- bloqade/stim/rewrite/wire_to_stim.py +0 -57
- {bloqade_circuit-0.7.12.dist-info → bloqade_circuit-0.8.0.dist-info}/WHEEL +0 -0
- {bloqade_circuit-0.7.12.dist-info → bloqade_circuit-0.8.0.dist-info}/licenses/LICENSE +0 -0
bloqade/squin/cirq/lowering.py
DELETED
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
import math
|
|
2
|
-
from typing import Any
|
|
3
|
-
from dataclasses import field, dataclass
|
|
4
|
-
|
|
5
|
-
import cirq
|
|
6
|
-
from kirin import ir, types, lowering
|
|
7
|
-
from kirin.rewrite import Walk, CFGCompactify
|
|
8
|
-
from kirin.dialects import py, scf, ilist
|
|
9
|
-
|
|
10
|
-
from .. import op, noise, qubit
|
|
11
|
-
|
|
12
|
-
CirqNode = cirq.Circuit | cirq.Moment | cirq.Gate | cirq.Qid | cirq.Operation
|
|
13
|
-
|
|
14
|
-
DecomposeNode = (
|
|
15
|
-
cirq.SwapPowGate
|
|
16
|
-
| cirq.ISwapPowGate
|
|
17
|
-
| cirq.PhasedXPowGate
|
|
18
|
-
| cirq.PhasedXZGate
|
|
19
|
-
| cirq.CSwapGate
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@dataclass
|
|
24
|
-
class Squin(lowering.LoweringABC[CirqNode]):
|
|
25
|
-
"""Lower a cirq.Circuit object to a squin kernel"""
|
|
26
|
-
|
|
27
|
-
circuit: cirq.Circuit
|
|
28
|
-
qreg: ir.SSAValue = field(init=False)
|
|
29
|
-
qreg_index: dict[cirq.Qid, int] = field(init=False, default_factory=dict)
|
|
30
|
-
next_qreg_index: int = field(init=False, default=0)
|
|
31
|
-
|
|
32
|
-
def __post_init__(self):
|
|
33
|
-
# TODO: sort by cirq ordering
|
|
34
|
-
qbits = sorted(self.circuit.all_qubits())
|
|
35
|
-
self.qreg_index = {qid: idx for (idx, qid) in enumerate(qbits)}
|
|
36
|
-
|
|
37
|
-
def lower_qubit_getindex(self, state: lowering.State[CirqNode], qid: cirq.Qid):
|
|
38
|
-
index = self.qreg_index[qid]
|
|
39
|
-
index_ssa = state.current_frame.push(py.Constant(index)).result
|
|
40
|
-
qbit_getitem = state.current_frame.push(py.GetItem(self.qreg, index_ssa))
|
|
41
|
-
return qbit_getitem.result
|
|
42
|
-
|
|
43
|
-
def lower_qubit_getindices(
|
|
44
|
-
self, state: lowering.State[CirqNode], qids: list[cirq.Qid]
|
|
45
|
-
):
|
|
46
|
-
qbits_getitem = [self.lower_qubit_getindex(state, qid) for qid in qids]
|
|
47
|
-
return tuple(qbits_getitem)
|
|
48
|
-
|
|
49
|
-
def run(
|
|
50
|
-
self,
|
|
51
|
-
stmt: CirqNode,
|
|
52
|
-
*,
|
|
53
|
-
source: str | None = None,
|
|
54
|
-
globals: dict[str, Any] | None = None,
|
|
55
|
-
file: str | None = None,
|
|
56
|
-
lineno_offset: int = 0,
|
|
57
|
-
col_offset: int = 0,
|
|
58
|
-
compactify: bool = True,
|
|
59
|
-
register_as_argument: bool = False,
|
|
60
|
-
register_argument_name: str = "q",
|
|
61
|
-
) -> ir.Region:
|
|
62
|
-
|
|
63
|
-
state = lowering.State(
|
|
64
|
-
self,
|
|
65
|
-
file=file,
|
|
66
|
-
lineno_offset=lineno_offset,
|
|
67
|
-
col_offset=col_offset,
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
with state.frame([stmt], globals=globals, finalize_next=False) as frame:
|
|
71
|
-
|
|
72
|
-
# NOTE: need a register of qubits before lowering statements
|
|
73
|
-
if register_as_argument:
|
|
74
|
-
# NOTE: register as argument to the kernel; we have freedom of choice for the name here
|
|
75
|
-
frame.curr_block.args.append_from(
|
|
76
|
-
ilist.IListType[qubit.QubitType, types.Any],
|
|
77
|
-
name=register_argument_name,
|
|
78
|
-
)
|
|
79
|
-
self.qreg = frame.curr_block.args[0]
|
|
80
|
-
else:
|
|
81
|
-
# NOTE: create a new register of appropriate size
|
|
82
|
-
n_qubits = len(self.qreg_index)
|
|
83
|
-
n = frame.push(py.Constant(n_qubits))
|
|
84
|
-
self.qreg = frame.push(qubit.New(n_qubits=n.result)).result
|
|
85
|
-
|
|
86
|
-
self.visit(state, stmt)
|
|
87
|
-
|
|
88
|
-
if compactify:
|
|
89
|
-
Walk(CFGCompactify()).rewrite(frame.curr_region)
|
|
90
|
-
|
|
91
|
-
region = frame.curr_region
|
|
92
|
-
|
|
93
|
-
return region
|
|
94
|
-
|
|
95
|
-
def visit(self, state: lowering.State[CirqNode], node: CirqNode) -> lowering.Result:
|
|
96
|
-
name = node.__class__.__name__
|
|
97
|
-
return getattr(self, f"visit_{name}", self.generic_visit)(state, node)
|
|
98
|
-
|
|
99
|
-
def generic_visit(self, state: lowering.State[CirqNode], node: CirqNode):
|
|
100
|
-
if isinstance(node, CirqNode):
|
|
101
|
-
raise lowering.BuildError(
|
|
102
|
-
f"Cannot lower {node.__class__.__name__} node: {node}"
|
|
103
|
-
)
|
|
104
|
-
raise lowering.BuildError(
|
|
105
|
-
f"Unexpected `{node.__class__.__name__}` node: {repr(node)} is not an AST node"
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
def lower_literal(self, state: lowering.State[CirqNode], value) -> ir.SSAValue:
|
|
109
|
-
raise lowering.BuildError("Literals not supported in cirq circuit")
|
|
110
|
-
|
|
111
|
-
def lower_global(
|
|
112
|
-
self, state: lowering.State[CirqNode], node: CirqNode
|
|
113
|
-
) -> lowering.LoweringABC.Result:
|
|
114
|
-
raise lowering.BuildError("Literals not supported in cirq circuit")
|
|
115
|
-
|
|
116
|
-
def visit_Circuit(
|
|
117
|
-
self, state: lowering.State[CirqNode], node: cirq.Circuit
|
|
118
|
-
) -> lowering.Result:
|
|
119
|
-
for moment in node:
|
|
120
|
-
state.lower(moment)
|
|
121
|
-
|
|
122
|
-
def visit_Moment(
|
|
123
|
-
self, state: lowering.State[CirqNode], node: cirq.Moment
|
|
124
|
-
) -> lowering.Result:
|
|
125
|
-
for op_ in node.operations:
|
|
126
|
-
state.lower(op_)
|
|
127
|
-
|
|
128
|
-
def visit_GateOperation(
|
|
129
|
-
self, state: lowering.State[CirqNode], node: cirq.GateOperation
|
|
130
|
-
):
|
|
131
|
-
if isinstance(node.gate, cirq.MeasurementGate):
|
|
132
|
-
# NOTE: special dispatch here, since measurement is a gate + a qubit in cirq,
|
|
133
|
-
# but a single statement in squin
|
|
134
|
-
return self.lower_measurement(state, node)
|
|
135
|
-
|
|
136
|
-
if isinstance(node.gate, DecomposeNode):
|
|
137
|
-
# NOTE: easier to decompose these, but for that we need the qubits too,
|
|
138
|
-
# so we need to do this within this method
|
|
139
|
-
for subnode in cirq.decompose_once(node):
|
|
140
|
-
state.lower(subnode)
|
|
141
|
-
return
|
|
142
|
-
|
|
143
|
-
op_ = state.lower(node.gate).expect_one()
|
|
144
|
-
qbits = self.lower_qubit_getindices(state, node.qubits)
|
|
145
|
-
return state.current_frame.push(qubit.Apply(operator=op_, qubits=qbits))
|
|
146
|
-
|
|
147
|
-
def visit_TaggedOperation(
|
|
148
|
-
self, state: lowering.State[CirqNode], node: cirq.TaggedOperation
|
|
149
|
-
):
|
|
150
|
-
state.lower(node.untagged)
|
|
151
|
-
|
|
152
|
-
def lower_measurement(
|
|
153
|
-
self, state: lowering.State[CirqNode], node: cirq.GateOperation
|
|
154
|
-
):
|
|
155
|
-
if len(node.qubits) == 1:
|
|
156
|
-
qbit = self.lower_qubit_getindex(state, node.qubits[0])
|
|
157
|
-
stmt = state.current_frame.push(qubit.MeasureQubit(qbit))
|
|
158
|
-
else:
|
|
159
|
-
qbits = self.lower_qubit_getindices(state, node.qubits)
|
|
160
|
-
qbits_list = state.current_frame.push(ilist.New(values=qbits))
|
|
161
|
-
stmt = state.current_frame.push(qubit.MeasureQubitList(qbits_list.result))
|
|
162
|
-
|
|
163
|
-
key = node.gate.key
|
|
164
|
-
if isinstance(key, cirq.MeasurementKey):
|
|
165
|
-
key = key.name
|
|
166
|
-
|
|
167
|
-
state.current_frame.defs[key] = stmt.result
|
|
168
|
-
return stmt
|
|
169
|
-
|
|
170
|
-
def visit_ClassicallyControlledOperation(
|
|
171
|
-
self, state: lowering.State[CirqNode], node: cirq.ClassicallyControlledOperation
|
|
172
|
-
):
|
|
173
|
-
conditions: list[ir.SSAValue] = []
|
|
174
|
-
for outcome in node.classical_controls:
|
|
175
|
-
key = outcome.key
|
|
176
|
-
if isinstance(key, cirq.MeasurementKey):
|
|
177
|
-
key = key.name
|
|
178
|
-
measurement_outcome = state.current_frame.defs[key]
|
|
179
|
-
|
|
180
|
-
if measurement_outcome.type.is_subseteq(ilist.IListType):
|
|
181
|
-
# NOTE: there is currently no convenient ilist.any method, so we need to use foldl
|
|
182
|
-
# with a simple function that just does an or
|
|
183
|
-
|
|
184
|
-
def bool_op_or(x: bool, y: bool) -> bool:
|
|
185
|
-
return x or y
|
|
186
|
-
|
|
187
|
-
f_code = state.current_frame.push(
|
|
188
|
-
lowering.Python(self.dialects).python_function(bool_op_or)
|
|
189
|
-
)
|
|
190
|
-
fn = ir.Method(
|
|
191
|
-
mod=None,
|
|
192
|
-
py_func=bool_op_or,
|
|
193
|
-
sym_name="bool_op_or",
|
|
194
|
-
arg_names=[],
|
|
195
|
-
dialects=self.dialects,
|
|
196
|
-
code=f_code,
|
|
197
|
-
)
|
|
198
|
-
f_const = state.current_frame.push(py.constant.Constant(fn))
|
|
199
|
-
init_val = state.current_frame.push(py.Constant(False)).result
|
|
200
|
-
condition = state.current_frame.push(
|
|
201
|
-
ilist.Foldl(f_const.result, measurement_outcome, init=init_val)
|
|
202
|
-
).result
|
|
203
|
-
else:
|
|
204
|
-
condition = measurement_outcome
|
|
205
|
-
|
|
206
|
-
conditions.append(condition)
|
|
207
|
-
|
|
208
|
-
if len(conditions) == 1:
|
|
209
|
-
condition = conditions[0]
|
|
210
|
-
else:
|
|
211
|
-
condition = state.current_frame.push(
|
|
212
|
-
py.boolop.And(conditions[0], conditions[1])
|
|
213
|
-
).result
|
|
214
|
-
for next_cond in conditions[2:]:
|
|
215
|
-
condition = state.current_frame.push(
|
|
216
|
-
py.boolop.And(condition, next_cond)
|
|
217
|
-
).result
|
|
218
|
-
|
|
219
|
-
then_stmt = self.visit(state, node.without_classical_controls())
|
|
220
|
-
|
|
221
|
-
assert isinstance(
|
|
222
|
-
then_stmt, ir.Statement
|
|
223
|
-
), f"Expected operation of classically controlled node {node} to be lowered to a statement, got type {type(then_stmt)}. \
|
|
224
|
-
Please report this issue!"
|
|
225
|
-
|
|
226
|
-
# NOTE: remove stmt from parent block
|
|
227
|
-
then_stmt.detach()
|
|
228
|
-
then_body = ir.Block((then_stmt,))
|
|
229
|
-
|
|
230
|
-
return state.current_frame.push(scf.IfElse(condition, then_body=then_body))
|
|
231
|
-
|
|
232
|
-
def visit_SingleQubitPauliStringGateOperation(
|
|
233
|
-
self,
|
|
234
|
-
state: lowering.State[CirqNode],
|
|
235
|
-
node: cirq.SingleQubitPauliStringGateOperation,
|
|
236
|
-
):
|
|
237
|
-
|
|
238
|
-
match node.pauli:
|
|
239
|
-
case cirq.X:
|
|
240
|
-
op_ = op.stmts.X()
|
|
241
|
-
case cirq.Y:
|
|
242
|
-
op_ = op.stmts.Y()
|
|
243
|
-
case cirq.Z:
|
|
244
|
-
op_ = op.stmts.Z()
|
|
245
|
-
case cirq.I:
|
|
246
|
-
op_ = op.stmts.Identity(sites=1)
|
|
247
|
-
case _:
|
|
248
|
-
raise lowering.BuildError(f"Unexpected Pauli operation {node.pauli}")
|
|
249
|
-
|
|
250
|
-
state.current_frame.push(op_)
|
|
251
|
-
qargs = self.lower_qubit_getindices(state, [node.qubit])
|
|
252
|
-
return state.current_frame.push(qubit.Apply(op_.result, qargs))
|
|
253
|
-
|
|
254
|
-
def visit_HPowGate(self, state: lowering.State[CirqNode], node: cirq.HPowGate):
|
|
255
|
-
if abs(node.exponent) == 1:
|
|
256
|
-
return state.current_frame.push(op.stmts.H())
|
|
257
|
-
|
|
258
|
-
# NOTE: decompose into products of paulis for arbitrary exponents according to _decompose_ method
|
|
259
|
-
# can't use decompose directly since that method requires qubits to be passed in for some reason
|
|
260
|
-
y_rhs = state.lower(cirq.YPowGate(exponent=0.25)).expect_one()
|
|
261
|
-
x = state.lower(
|
|
262
|
-
cirq.XPowGate(exponent=node.exponent, global_shift=node.global_shift)
|
|
263
|
-
).expect_one()
|
|
264
|
-
y_lhs = state.lower(cirq.YPowGate(exponent=-0.25)).expect_one()
|
|
265
|
-
|
|
266
|
-
# NOTE: reversed order since we're creating a mult stmt
|
|
267
|
-
m_lhs = state.current_frame.push(op.stmts.Mult(y_lhs, x))
|
|
268
|
-
return state.current_frame.push(op.stmts.Mult(m_lhs.result, y_rhs))
|
|
269
|
-
|
|
270
|
-
def visit_XPowGate(self, state: lowering.State[CirqNode], node: cirq.XPowGate):
|
|
271
|
-
if abs(node.exponent == 1):
|
|
272
|
-
return state.current_frame.push(op.stmts.X())
|
|
273
|
-
|
|
274
|
-
return self.visit(state, node.in_su2())
|
|
275
|
-
|
|
276
|
-
def visit_YPowGate(self, state: lowering.State[CirqNode], node: cirq.YPowGate):
|
|
277
|
-
if abs(node.exponent == 1):
|
|
278
|
-
return state.current_frame.push(op.stmts.Y())
|
|
279
|
-
|
|
280
|
-
return self.visit(state, node.in_su2())
|
|
281
|
-
|
|
282
|
-
def visit_ZPowGate(self, state: lowering.State[CirqNode], node: cirq.ZPowGate):
|
|
283
|
-
if node.exponent == 0.5:
|
|
284
|
-
return state.current_frame.push(op.stmts.S())
|
|
285
|
-
|
|
286
|
-
if node.exponent == 0.25:
|
|
287
|
-
return state.current_frame.push(op.stmts.T())
|
|
288
|
-
|
|
289
|
-
if abs(node.exponent == 1):
|
|
290
|
-
return state.current_frame.push(op.stmts.Z())
|
|
291
|
-
|
|
292
|
-
# NOTE: just for the Z gate, an arbitrary exponent is equivalent to the ShiftOp
|
|
293
|
-
# up to a minus sign!
|
|
294
|
-
t = -node.exponent
|
|
295
|
-
theta = state.current_frame.push(py.Constant(math.pi * t))
|
|
296
|
-
return state.current_frame.push(op.stmts.ShiftOp(theta=theta.result))
|
|
297
|
-
|
|
298
|
-
def visit_Rx(self, state: lowering.State[CirqNode], node: cirq.Rx):
|
|
299
|
-
x = state.current_frame.push(op.stmts.X())
|
|
300
|
-
angle = state.current_frame.push(py.Constant(value=math.pi * node.exponent))
|
|
301
|
-
return state.current_frame.push(op.stmts.Rot(axis=x.result, angle=angle.result))
|
|
302
|
-
|
|
303
|
-
def visit_Ry(self, state: lowering.State[CirqNode], node: cirq.Ry):
|
|
304
|
-
y = state.current_frame.push(op.stmts.Y())
|
|
305
|
-
angle = state.current_frame.push(py.Constant(value=math.pi * node.exponent))
|
|
306
|
-
return state.current_frame.push(op.stmts.Rot(axis=y.result, angle=angle.result))
|
|
307
|
-
|
|
308
|
-
def visit_Rz(self, state: lowering.State[CirqNode], node: cirq.Rz):
|
|
309
|
-
z = state.current_frame.push(op.stmts.Z())
|
|
310
|
-
angle = state.current_frame.push(py.Constant(value=math.pi * node.exponent))
|
|
311
|
-
return state.current_frame.push(op.stmts.Rot(axis=z.result, angle=angle.result))
|
|
312
|
-
|
|
313
|
-
def visit_CXPowGate(self, state: lowering.State[CirqNode], node: cirq.CXPowGate):
|
|
314
|
-
x = state.lower(cirq.XPowGate(exponent=node.exponent)).expect_one()
|
|
315
|
-
return state.current_frame.push(op.stmts.Control(x, n_controls=1))
|
|
316
|
-
|
|
317
|
-
def visit_CZPowGate(self, state: lowering.State[CirqNode], node: cirq.CZPowGate):
|
|
318
|
-
z = state.lower(cirq.ZPowGate(exponent=node.exponent)).expect_one()
|
|
319
|
-
return state.current_frame.push(op.stmts.Control(z, n_controls=1))
|
|
320
|
-
|
|
321
|
-
def visit_ControlledOperation(
|
|
322
|
-
self, state: lowering.State[CirqNode], node: cirq.ControlledOperation
|
|
323
|
-
):
|
|
324
|
-
return self.visit_GateOperation(state, node)
|
|
325
|
-
|
|
326
|
-
def visit_ControlledGate(
|
|
327
|
-
self, state: lowering.State[CirqNode], node: cirq.ControlledGate
|
|
328
|
-
):
|
|
329
|
-
op_ = state.lower(node.sub_gate).expect_one()
|
|
330
|
-
n_controls = node.num_controls()
|
|
331
|
-
return state.current_frame.push(op.stmts.Control(op_, n_controls=n_controls))
|
|
332
|
-
|
|
333
|
-
def visit_XXPowGate(self, state: lowering.State[CirqNode], node: cirq.XXPowGate):
|
|
334
|
-
x = state.lower(cirq.XPowGate(exponent=node.exponent)).expect_one()
|
|
335
|
-
return state.current_frame.push(op.stmts.Kron(x, x))
|
|
336
|
-
|
|
337
|
-
def visit_YYPowGate(self, state: lowering.State[CirqNode], node: cirq.YYPowGate):
|
|
338
|
-
y = state.lower(cirq.YPowGate(exponent=node.exponent)).expect_one()
|
|
339
|
-
return state.current_frame.push(op.stmts.Kron(y, y))
|
|
340
|
-
|
|
341
|
-
def visit_ZZPowGate(self, state: lowering.State[CirqNode], node: cirq.ZZPowGate):
|
|
342
|
-
z = state.lower(cirq.ZPowGate(exponent=node.exponent)).expect_one()
|
|
343
|
-
return state.current_frame.push(op.stmts.Kron(z, z))
|
|
344
|
-
|
|
345
|
-
def visit_CCXPowGate(self, state: lowering.State[CirqNode], node: cirq.CCXPowGate):
|
|
346
|
-
x = state.lower(cirq.XPowGate(exponent=node.exponent)).expect_one()
|
|
347
|
-
return state.current_frame.push(op.stmts.Control(x, n_controls=2))
|
|
348
|
-
|
|
349
|
-
def visit_CCZPowGate(self, state: lowering.State[CirqNode], node: cirq.CCZPowGate):
|
|
350
|
-
z = state.lower(cirq.ZPowGate(exponent=node.exponent)).expect_one()
|
|
351
|
-
return state.current_frame.push(op.stmts.Control(z, n_controls=2))
|
|
352
|
-
|
|
353
|
-
def visit_BitFlipChannel(
|
|
354
|
-
self, state: lowering.State[CirqNode], node: cirq.BitFlipChannel
|
|
355
|
-
):
|
|
356
|
-
x = state.current_frame.push(op.stmts.X())
|
|
357
|
-
p = state.current_frame.push(py.Constant(node.p))
|
|
358
|
-
return state.current_frame.push(
|
|
359
|
-
noise.stmts.PauliError(basis=x.result, p=p.result)
|
|
360
|
-
)
|
|
361
|
-
|
|
362
|
-
def visit_AmplitudeDampingChannel(
|
|
363
|
-
self, state: lowering.State[CirqNode], node: cirq.AmplitudeDampingChannel
|
|
364
|
-
):
|
|
365
|
-
r = state.current_frame.push(op.stmts.Reset())
|
|
366
|
-
p = state.current_frame.push(py.Constant(node.gamma))
|
|
367
|
-
|
|
368
|
-
# TODO: do we need a dedicated noise stmt for this? Using PauliError
|
|
369
|
-
# with this basis feels like a hack
|
|
370
|
-
noise_channel = state.current_frame.push(
|
|
371
|
-
noise.stmts.PauliError(basis=r.result, p=p.result)
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
return noise_channel
|
|
375
|
-
|
|
376
|
-
def visit_GeneralizedAmplitudeDampingChannel(
|
|
377
|
-
self,
|
|
378
|
-
state: lowering.State[CirqNode],
|
|
379
|
-
node: cirq.GeneralizedAmplitudeDampingChannel,
|
|
380
|
-
):
|
|
381
|
-
p = state.current_frame.push(py.Constant(node.p)).result
|
|
382
|
-
gamma = state.current_frame.push(py.Constant(node.gamma)).result
|
|
383
|
-
|
|
384
|
-
# NOTE: cirq has a weird convention here: if p == 1, we have AmplitudeDampingChannel,
|
|
385
|
-
# which basically means p is the probability of the environment being in the vacuum state
|
|
386
|
-
prob0 = state.current_frame.push(py.binop.Mult(p, gamma)).result
|
|
387
|
-
one_ = state.current_frame.push(py.Constant(1)).result
|
|
388
|
-
p_minus_1 = state.current_frame.push(py.binop.Sub(one_, p)).result
|
|
389
|
-
prob1 = state.current_frame.push(py.binop.Mult(p_minus_1, gamma)).result
|
|
390
|
-
|
|
391
|
-
r0 = state.current_frame.push(op.stmts.Reset()).result
|
|
392
|
-
r1 = state.current_frame.push(op.stmts.ResetToOne()).result
|
|
393
|
-
|
|
394
|
-
probs = state.current_frame.push(ilist.New(values=(prob0, prob1))).result
|
|
395
|
-
ops = state.current_frame.push(ilist.New(values=(r0, r1))).result
|
|
396
|
-
|
|
397
|
-
noise_channel = state.current_frame.push(
|
|
398
|
-
noise.stmts.StochasticUnitaryChannel(probabilities=probs, operators=ops)
|
|
399
|
-
)
|
|
400
|
-
|
|
401
|
-
return noise_channel
|
|
402
|
-
|
|
403
|
-
def visit_DepolarizingChannel(
|
|
404
|
-
self, state: lowering.State[CirqNode], node: cirq.DepolarizingChannel
|
|
405
|
-
):
|
|
406
|
-
p = state.current_frame.push(py.Constant(node.p)).result
|
|
407
|
-
return state.current_frame.push(noise.stmts.Depolarize(p))
|
|
408
|
-
|
|
409
|
-
def visit_AsymmetricDepolarizingChannel(
|
|
410
|
-
self, state: lowering.State[CirqNode], node: cirq.AsymmetricDepolarizingChannel
|
|
411
|
-
):
|
|
412
|
-
nqubits = node.num_qubits()
|
|
413
|
-
if nqubits > 2:
|
|
414
|
-
raise lowering.BuildError(
|
|
415
|
-
"AsymmetricDepolarizingChannel applied to more than 2 qubits is not supported!"
|
|
416
|
-
)
|
|
417
|
-
|
|
418
|
-
if nqubits == 1:
|
|
419
|
-
p_x = state.current_frame.push(py.Constant(node.p_x)).result
|
|
420
|
-
p_y = state.current_frame.push(py.Constant(node.p_y)).result
|
|
421
|
-
p_z = state.current_frame.push(py.Constant(node.p_z)).result
|
|
422
|
-
params = state.current_frame.push(ilist.New(values=(p_x, p_y, p_z))).result
|
|
423
|
-
return state.current_frame.push(noise.stmts.SingleQubitPauliChannel(params))
|
|
424
|
-
|
|
425
|
-
# NOTE: nqubits == 2
|
|
426
|
-
error_probs = node.error_probabilities
|
|
427
|
-
paulis = ("I", "X", "Y", "Z")
|
|
428
|
-
values = []
|
|
429
|
-
for p1 in paulis:
|
|
430
|
-
for p2 in paulis:
|
|
431
|
-
if p1 == p2 == "I":
|
|
432
|
-
continue
|
|
433
|
-
|
|
434
|
-
p = error_probs.get(p1 + p2, 0.0)
|
|
435
|
-
p_ssa = state.current_frame.push(py.Constant(p)).result
|
|
436
|
-
values.append(p_ssa)
|
|
437
|
-
|
|
438
|
-
params = state.current_frame.push(ilist.New(values=values)).result
|
|
439
|
-
return state.current_frame.push(noise.stmts.TwoQubitPauliChannel(params))
|
bloqade/squin/lowering.py
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import ast
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
|
|
4
|
-
from kirin import lowering
|
|
5
|
-
|
|
6
|
-
from . import qubit
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@dataclass(frozen=True)
|
|
10
|
-
class ApplyAnyCallLowering(lowering.FromPythonCall["qubit.ApplyAny"]):
|
|
11
|
-
"""
|
|
12
|
-
Custom lowering for ApplyAny that collects vararg qubits into a single tuple argument
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
def lower(
|
|
16
|
-
self, stmt: type["qubit.ApplyAny"], state: lowering.State, node: ast.Call
|
|
17
|
-
):
|
|
18
|
-
if len(node.args) + len(node.keywords) < 2:
|
|
19
|
-
raise lowering.BuildError(
|
|
20
|
-
"Apply requires at least one operator and one qubit as arguments!"
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
op, qubits = self.unpack_arguments(node)
|
|
24
|
-
|
|
25
|
-
op_ssa = state.lower(op).expect_one()
|
|
26
|
-
qubits_lowered = [state.lower(qbit).expect_one() for qbit in qubits]
|
|
27
|
-
|
|
28
|
-
s = stmt(op_ssa, tuple(qubits_lowered))
|
|
29
|
-
return state.current_frame.push(s)
|
|
30
|
-
|
|
31
|
-
def unpack_arguments(self, node: ast.Call) -> tuple[ast.expr, list[ast.expr]]:
|
|
32
|
-
if len(node.keywords) == 0:
|
|
33
|
-
op, *qubits = node.args
|
|
34
|
-
return op, qubits
|
|
35
|
-
|
|
36
|
-
kwargs = {kw.arg: kw.value for kw in node.keywords}
|
|
37
|
-
if len(kwargs) > 2 or "qubits" not in kwargs:
|
|
38
|
-
raise lowering.BuildError(f"Got unsupported keyword argument {kwargs}")
|
|
39
|
-
|
|
40
|
-
qubits = kwargs["qubits"]
|
|
41
|
-
if len(kwargs) == 1:
|
|
42
|
-
if len(node.args) != 1:
|
|
43
|
-
raise lowering.BuildError("Missing operator argument")
|
|
44
|
-
op = node.args[0]
|
|
45
|
-
else:
|
|
46
|
-
try:
|
|
47
|
-
op = kwargs["operator"]
|
|
48
|
-
except KeyError:
|
|
49
|
-
raise lowering.BuildError(f"Got unsupported keyword argument {kwargs}")
|
|
50
|
-
|
|
51
|
-
if isinstance(qubits, ast.List):
|
|
52
|
-
return op, qubits.elts
|
|
53
|
-
|
|
54
|
-
return op, [qubits]
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@dataclass(frozen=True)
|
|
58
|
-
class BroadcastCallLowering(lowering.FromPythonCall["qubit.Broadcast"]):
|
|
59
|
-
"""
|
|
60
|
-
Custom lowering for broadcast vararg call.
|
|
61
|
-
|
|
62
|
-
NOTE: we can re-use this to lower Apply too once we remove the deprecated syntax
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
def lower(
|
|
66
|
-
self, stmt: type["qubit.Broadcast"], state: lowering.State, node: ast.Call
|
|
67
|
-
):
|
|
68
|
-
if len(node.args) < 2:
|
|
69
|
-
raise lowering.BuildError(
|
|
70
|
-
"Broadcast requires at least one operator and one qubit list argument"
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
op, *qubit_lists = node.args
|
|
74
|
-
|
|
75
|
-
op_lowered = state.lower(op).expect_one()
|
|
76
|
-
qubits_lists_lowered = [
|
|
77
|
-
state.lower(qubit_list).expect_one() for qubit_list in qubit_lists
|
|
78
|
-
]
|
|
79
|
-
|
|
80
|
-
return state.current_frame.push(stmt(op_lowered, tuple(qubits_lists_lowered)))
|
bloqade/squin/noise/_wrapper.py
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
from typing import Literal
|
|
2
|
-
|
|
3
|
-
from kirin.dialects import ilist
|
|
4
|
-
from kirin.lowering import wraps
|
|
5
|
-
|
|
6
|
-
from bloqade.squin.op.types import Op, MultiQubitPauliOp
|
|
7
|
-
|
|
8
|
-
from . import stmts
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@wraps(stmts.PauliError)
|
|
12
|
-
def pauli_error(basis: MultiQubitPauliOp, p: float) -> Op: ...
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@wraps(stmts.Depolarize)
|
|
16
|
-
def depolarize(p: float) -> Op: ...
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@wraps(stmts.Depolarize2)
|
|
20
|
-
def depolarize2(p: float) -> Op: ...
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@wraps(stmts.SingleQubitPauliChannel)
|
|
24
|
-
def single_qubit_pauli_channel(
|
|
25
|
-
params: ilist.IList[float, Literal[3]] | list[float],
|
|
26
|
-
) -> Op: ...
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@wraps(stmts.TwoQubitPauliChannel)
|
|
30
|
-
def two_qubit_pauli_channel(
|
|
31
|
-
params: ilist.IList[float, Literal[15]] | list[float],
|
|
32
|
-
) -> Op: ...
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@wraps(stmts.QubitLoss)
|
|
36
|
-
def qubit_loss(p: float) -> Op: ...
|
bloqade/squin/noise/rewrite.py
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import itertools
|
|
2
|
-
|
|
3
|
-
from kirin import ir
|
|
4
|
-
from kirin.passes import Pass
|
|
5
|
-
from kirin.rewrite import Walk
|
|
6
|
-
from kirin.dialects import py, ilist
|
|
7
|
-
from kirin.rewrite.abc import RewriteRule, RewriteResult
|
|
8
|
-
|
|
9
|
-
from .stmts import (
|
|
10
|
-
QubitLoss,
|
|
11
|
-
Depolarize,
|
|
12
|
-
PauliError,
|
|
13
|
-
Depolarize2,
|
|
14
|
-
NoiseChannel,
|
|
15
|
-
TwoQubitPauliChannel,
|
|
16
|
-
SingleQubitPauliChannel,
|
|
17
|
-
StochasticUnitaryChannel,
|
|
18
|
-
)
|
|
19
|
-
from ..op.stmts import X, Y, Z, Kron, Identity
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class _RewriteNoiseStmts(RewriteRule):
|
|
23
|
-
"""Rewrites squin noise statements to StochasticUnitaryChannel"""
|
|
24
|
-
|
|
25
|
-
def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
|
|
26
|
-
if not isinstance(node, NoiseChannel) or isinstance(node, QubitLoss):
|
|
27
|
-
return RewriteResult()
|
|
28
|
-
|
|
29
|
-
return getattr(self, "rewrite_" + node.name)(node)
|
|
30
|
-
|
|
31
|
-
def rewrite_pauli_error(self, node: PauliError) -> RewriteResult:
|
|
32
|
-
(operators := ilist.New(values=(node.basis,))).insert_before(node)
|
|
33
|
-
(ps := ilist.New(values=(node.p,))).insert_before(node)
|
|
34
|
-
stochastic_channel = StochasticUnitaryChannel(
|
|
35
|
-
operators=operators.result, probabilities=ps.result
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
node.replace_by(stochastic_channel)
|
|
39
|
-
return RewriteResult(has_done_something=True)
|
|
40
|
-
|
|
41
|
-
def rewrite_single_qubit_pauli_channel(
|
|
42
|
-
self, node: SingleQubitPauliChannel
|
|
43
|
-
) -> RewriteResult:
|
|
44
|
-
paulis = (X(), Y(), Z())
|
|
45
|
-
paulis_ssa: list[ir.SSAValue] = []
|
|
46
|
-
for op in paulis:
|
|
47
|
-
op.insert_before(node)
|
|
48
|
-
paulis_ssa.append(op.result)
|
|
49
|
-
|
|
50
|
-
(pauli_ops := ilist.New(values=paulis_ssa)).insert_before(node)
|
|
51
|
-
|
|
52
|
-
stochastic_unitary = StochasticUnitaryChannel(
|
|
53
|
-
operators=pauli_ops.result, probabilities=node.params
|
|
54
|
-
)
|
|
55
|
-
node.replace_by(stochastic_unitary)
|
|
56
|
-
return RewriteResult(has_done_something=True)
|
|
57
|
-
|
|
58
|
-
def rewrite_two_qubit_pauli_channel(
|
|
59
|
-
self, node: TwoQubitPauliChannel
|
|
60
|
-
) -> RewriteResult:
|
|
61
|
-
operator_list = self._insert_two_qubit_paulis_before_node(node)
|
|
62
|
-
stochastic_unitary = StochasticUnitaryChannel(
|
|
63
|
-
operators=operator_list, probabilities=node.params
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
node.replace_by(stochastic_unitary)
|
|
67
|
-
return RewriteResult(has_done_something=True)
|
|
68
|
-
|
|
69
|
-
@staticmethod
|
|
70
|
-
def _insert_two_qubit_paulis_before_node(
|
|
71
|
-
node: TwoQubitPauliChannel | Depolarize2,
|
|
72
|
-
) -> ir.ResultValue:
|
|
73
|
-
paulis = (Identity(sites=1), X(), Y(), Z())
|
|
74
|
-
for op in paulis:
|
|
75
|
-
op.insert_before(node)
|
|
76
|
-
|
|
77
|
-
# NOTE: collect list so we can skip the first entry, which will be two identities
|
|
78
|
-
combinations = list(itertools.product(paulis, repeat=2))[1:]
|
|
79
|
-
operators: list[ir.SSAValue] = []
|
|
80
|
-
for pauli_1, pauli_2 in combinations:
|
|
81
|
-
op = Kron(pauli_1.result, pauli_2.result)
|
|
82
|
-
op.insert_before(node)
|
|
83
|
-
operators.append(op.result)
|
|
84
|
-
|
|
85
|
-
(operator_list := ilist.New(values=operators)).insert_before(node)
|
|
86
|
-
return operator_list.result
|
|
87
|
-
|
|
88
|
-
def rewrite_depolarize(self, node: Depolarize) -> RewriteResult:
|
|
89
|
-
paulis = (X(), Y(), Z())
|
|
90
|
-
operators: list[ir.SSAValue] = []
|
|
91
|
-
for op in paulis:
|
|
92
|
-
op.insert_before(node)
|
|
93
|
-
operators.append(op.result)
|
|
94
|
-
|
|
95
|
-
# NOTE: need to divide the probability by 3 to get the correct total error rate
|
|
96
|
-
(three := py.Constant(3)).insert_before(node)
|
|
97
|
-
(p_over_3 := py.Div(node.p, three.result)).insert_before(node)
|
|
98
|
-
|
|
99
|
-
(operator_list := ilist.New(values=operators)).insert_before(node)
|
|
100
|
-
(ps := ilist.New(values=[p_over_3.result for _ in range(3)])).insert_before(
|
|
101
|
-
node
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
stochastic_unitary = StochasticUnitaryChannel(
|
|
105
|
-
operators=operator_list.result, probabilities=ps.result
|
|
106
|
-
)
|
|
107
|
-
node.replace_by(stochastic_unitary)
|
|
108
|
-
|
|
109
|
-
return RewriteResult(has_done_something=True)
|
|
110
|
-
|
|
111
|
-
def rewrite_depolarize2(self, node: Depolarize2) -> RewriteResult:
|
|
112
|
-
operator_list = self._insert_two_qubit_paulis_before_node(node)
|
|
113
|
-
|
|
114
|
-
# NOTE: need to divide the probability by 15 to get the correct total error rate
|
|
115
|
-
(fifteen := py.Constant(15)).insert_before(node)
|
|
116
|
-
(p_over_15 := py.Div(node.p, fifteen.result)).insert_before(node)
|
|
117
|
-
(probs := ilist.New(values=[p_over_15.result] * 15)).insert_before(node)
|
|
118
|
-
|
|
119
|
-
stochastic_unitary = StochasticUnitaryChannel(
|
|
120
|
-
operators=operator_list, probabilities=probs.result
|
|
121
|
-
)
|
|
122
|
-
node.replace_by(stochastic_unitary)
|
|
123
|
-
|
|
124
|
-
return RewriteResult(has_done_something=True)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
class RewriteNoiseStmts(Pass):
|
|
128
|
-
def unsafe_run(self, mt: ir.Method):
|
|
129
|
-
return Walk(_RewriteNoiseStmts()).rewrite(mt.code)
|