bloqade-circuit 0.2.3__py3-none-any.whl → 0.4.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/impls.py +3 -2
- bloqade/pyqrack/device.py +1 -3
- bloqade/pyqrack/noise/native.py +8 -8
- bloqade/pyqrack/qasm2/core.py +4 -1
- bloqade/pyqrack/squin/op.py +7 -0
- bloqade/pyqrack/squin/qubit.py +5 -27
- bloqade/pyqrack/squin/runtime.py +18 -0
- bloqade/pyqrack/squin/wire.py +4 -22
- bloqade/pyqrack/task.py +13 -5
- bloqade/qasm2/__init__.py +1 -0
- bloqade/qasm2/_qasm_loading.py +151 -0
- bloqade/qasm2/dialects/core/__init__.py +9 -1
- bloqade/qasm2/dialects/expr/__init__.py +18 -1
- bloqade/{noise/native → qasm2/dialects/noise}/__init__.py +1 -7
- bloqade/qasm2/dialects/noise/_dialect.py +3 -0
- bloqade/{noise → qasm2/dialects/noise}/fidelity.py +4 -4
- bloqade/qasm2/dialects/noise/model.py +278 -0
- bloqade/{noise/native → qasm2/dialects/noise}/stmts.py +1 -1
- bloqade/qasm2/dialects/uop/__init__.py +39 -3
- bloqade/qasm2/dialects/uop/schedule.py +1 -1
- bloqade/qasm2/emit/impls/__init__.py +1 -0
- bloqade/qasm2/emit/impls/noise.py +89 -0
- bloqade/qasm2/emit/main.py +23 -4
- bloqade/qasm2/emit/target.py +19 -4
- bloqade/qasm2/noise.py +67 -0
- bloqade/qasm2/parse/__init__.py +7 -4
- bloqade/qasm2/parse/lowering.py +20 -130
- bloqade/qasm2/parse/qasm2.lark +1 -1
- bloqade/qasm2/passes/__init__.py +1 -0
- bloqade/qasm2/passes/fold.py +6 -0
- bloqade/qasm2/passes/glob.py +12 -8
- bloqade/qasm2/passes/noise.py +27 -16
- bloqade/qasm2/passes/parallel.py +9 -0
- bloqade/qasm2/passes/unroll_if.py +25 -0
- bloqade/qasm2/rewrite/__init__.py +3 -0
- bloqade/qasm2/rewrite/desugar.py +3 -2
- bloqade/qasm2/rewrite/native_gates.py +67 -4
- bloqade/qasm2/rewrite/noise/__init__.py +0 -0
- bloqade/qasm2/rewrite/{heuristic_noise.py → noise/heuristic_noise.py} +32 -62
- bloqade/{noise/native/rewrite.py → qasm2/rewrite/noise/remove_noise.py} +2 -2
- bloqade/qasm2/rewrite/split_ifs.py +66 -0
- bloqade/qbraid/lowering.py +8 -8
- bloqade/squin/__init__.py +7 -1
- bloqade/squin/analysis/nsites/__init__.py +1 -0
- bloqade/squin/analysis/nsites/impls.py +16 -1
- bloqade/squin/groups.py +4 -4
- bloqade/squin/lowering.py +27 -0
- bloqade/squin/noise/__init__.py +7 -26
- bloqade/squin/noise/_wrapper.py +25 -0
- bloqade/squin/op/__init__.py +34 -159
- bloqade/squin/op/_wrapper.py +105 -0
- bloqade/squin/op/stdlib.py +62 -0
- bloqade/squin/op/stmts.py +10 -0
- bloqade/squin/passes/__init__.py +1 -0
- bloqade/squin/passes/stim.py +68 -0
- bloqade/squin/qubit.py +32 -37
- bloqade/squin/rewrite/__init__.py +11 -0
- bloqade/squin/rewrite/desugar.py +65 -0
- bloqade/squin/rewrite/qubit_to_stim.py +61 -0
- bloqade/squin/rewrite/squin_measure.py +73 -0
- bloqade/squin/rewrite/stim_rewrite_util.py +153 -0
- bloqade/squin/rewrite/wire_identity_elimination.py +24 -0
- bloqade/squin/rewrite/wire_to_stim.py +52 -0
- bloqade/squin/rewrite/wrap_analysis.py +72 -0
- bloqade/squin/wire.py +5 -22
- bloqade/stim/__init__.py +40 -5
- bloqade/stim/_wrappers.py +18 -12
- bloqade/stim/dialects/__init__.py +1 -5
- bloqade/stim/dialects/{aux → auxiliary}/__init__.py +13 -1
- bloqade/stim/dialects/{aux → auxiliary}/emit.py +18 -3
- bloqade/stim/dialects/{aux → auxiliary}/stmts/__init__.py +1 -0
- bloqade/stim/dialects/{aux → auxiliary}/stmts/annotate.py +8 -0
- bloqade/stim/dialects/collapse/__init__.py +13 -2
- bloqade/stim/dialects/collapse/{emit.py → emit_str.py} +4 -2
- bloqade/stim/dialects/collapse/stmts/pp_measure.py +1 -1
- bloqade/stim/dialects/gate/__init__.py +16 -1
- bloqade/stim/dialects/gate/emit.py +10 -3
- bloqade/stim/dialects/gate/stmts/base.py +1 -1
- bloqade/stim/dialects/gate/stmts/pp.py +1 -1
- bloqade/stim/dialects/noise/emit.py +33 -2
- bloqade/stim/dialects/noise/stmts.py +29 -0
- bloqade/stim/emit/__init__.py +1 -1
- bloqade/stim/groups.py +4 -2
- bloqade/stim/parse/__init__.py +1 -0
- bloqade/stim/parse/lowering.py +686 -0
- {bloqade_circuit-0.2.3.dist-info → bloqade_circuit-0.4.0.dist-info}/METADATA +5 -3
- {bloqade_circuit-0.2.3.dist-info → bloqade_circuit-0.4.0.dist-info}/RECORD +95 -77
- bloqade/noise/__init__.py +0 -2
- bloqade/noise/native/_dialect.py +0 -3
- bloqade/noise/native/_wrappers.py +0 -34
- bloqade/noise/native/model.py +0 -346
- bloqade/qasm2/dialects/noise.py +0 -16
- bloqade/squin/rewrite/measure_desugar.py +0 -33
- /bloqade/stim/dialects/{aux → auxiliary}/_dialect.py +0 -0
- /bloqade/stim/dialects/{aux → auxiliary}/interp.py +0 -0
- /bloqade/stim/dialects/{aux → auxiliary}/lowering.py +0 -0
- /bloqade/stim/dialects/{aux → auxiliary}/stmts/const.py +0 -0
- /bloqade/stim/dialects/{aux → auxiliary}/types.py +0 -0
- /bloqade/stim/emit/{stim.py → stim_str.py} +0 -0
- {bloqade_circuit-0.2.3.dist-info → bloqade_circuit-0.4.0.dist-info}/WHEEL +0 -0
- {bloqade_circuit-0.2.3.dist-info → bloqade_circuit-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
"""One-to-one lowering routine from stim circuit to a stim-dialect kirin kernel."""
|
|
2
|
+
|
|
3
|
+
import pathlib
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Union
|
|
5
|
+
from dataclasses import field, dataclass
|
|
6
|
+
|
|
7
|
+
import kirin
|
|
8
|
+
from kirin import ir, lowering
|
|
9
|
+
from kirin.dialects import func
|
|
10
|
+
|
|
11
|
+
import bloqade.stim as kstim
|
|
12
|
+
from bloqade.stim.dialects import gate, noise, collapse, auxiliary
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
import stim
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
Node = Union["stim.Circuit", "stim.CircuitInstruction", "stim.GateTarget"]
|
|
19
|
+
LiteralType = Union[bool, int, float, str]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def loads(
|
|
23
|
+
stim_str: str,
|
|
24
|
+
*,
|
|
25
|
+
kernel_name: str = "main",
|
|
26
|
+
ignore_unknown_stim: bool = False,
|
|
27
|
+
error_unknown_nonstim: bool = False,
|
|
28
|
+
nonstim_noise_ops: dict[str, type[kirin.ir.Statement]] = {},
|
|
29
|
+
dialects: ir.DialectGroup | None = None,
|
|
30
|
+
globals: dict[str, Any] | None = None,
|
|
31
|
+
file: str | None = None,
|
|
32
|
+
lineno_offset: int = 0,
|
|
33
|
+
col_offset: int = 0,
|
|
34
|
+
compactify: bool = True,
|
|
35
|
+
) -> ir.Method[[], None]:
|
|
36
|
+
"""Loads a STIM string and returns the corresponding kernel object.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
stim_str: The string representation of a STIM circuit to load.
|
|
40
|
+
|
|
41
|
+
Keyword Args:
|
|
42
|
+
kernel_name (str): The name of the kernel to load. Defaults to "main".
|
|
43
|
+
ignore_unknown_stim (bool): If True, don't throw a build error on an
|
|
44
|
+
unimplemented stim instruction.
|
|
45
|
+
error_unknown_nonstim (bool): If True, throw a build error if an unknown tag is
|
|
46
|
+
used on the `I_ERROR` instruction.
|
|
47
|
+
nonstim_noise_ops (dict[str, kirin.ir.Statement]): Additional statements to
|
|
48
|
+
represent non-standard stim operations. The dictionary key should match the
|
|
49
|
+
tag used to identify it in stim (stim format
|
|
50
|
+
`I_ERROR[MY_NOISE](0.05) 0 1 2 3` or
|
|
51
|
+
`I_ERROR[MY_CORRELATED_NOISE:2417696374](0.03) 1 3 5`).
|
|
52
|
+
dialects (ir.DialectGroup | None): The dialects to use. Defaults to `stim.main`.
|
|
53
|
+
globals (dict[str, Any] | None): The global variables to use. Defaults to None.
|
|
54
|
+
file (str | None): The file name for error reporting. Defaults to None.
|
|
55
|
+
lineno_offset (int): The line number offset for error reporting. Defaults to 0.
|
|
56
|
+
col_offset (int): The column number offset for error reporting. Defaults to 0.
|
|
57
|
+
compactify (bool): Whether to compactify the output. Defaults to True.
|
|
58
|
+
|
|
59
|
+
Example:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from bloqade.stim.lowering import loads
|
|
63
|
+
method = loads('''
|
|
64
|
+
X 0 2 4
|
|
65
|
+
DEPOLARIZE1(0.01) 0
|
|
66
|
+
I_ERROR[CUSTOM_ERROR](0.02) 2 4
|
|
67
|
+
M 0 2 4
|
|
68
|
+
DETECTOR rec[-1] rec[-2]
|
|
69
|
+
''')
|
|
70
|
+
```
|
|
71
|
+
"""
|
|
72
|
+
import stim # Optional dependency required to lower stim circuits
|
|
73
|
+
|
|
74
|
+
circ = stim.Circuit(stim_str)
|
|
75
|
+
stim_lowering = Stim(
|
|
76
|
+
kstim.main if dialects is None else dialects,
|
|
77
|
+
ignore_unknown_stim=ignore_unknown_stim,
|
|
78
|
+
error_unknown_nonstim=error_unknown_nonstim,
|
|
79
|
+
nonstim_noise_ops=nonstim_noise_ops,
|
|
80
|
+
)
|
|
81
|
+
frame = stim_lowering.get_frame(
|
|
82
|
+
circ,
|
|
83
|
+
source=stim_str,
|
|
84
|
+
file=file,
|
|
85
|
+
globals=globals,
|
|
86
|
+
lineno_offset=lineno_offset,
|
|
87
|
+
col_offset=col_offset,
|
|
88
|
+
compactify=compactify,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return_value = func.ConstantNone() # No return value
|
|
92
|
+
frame.push(return_value)
|
|
93
|
+
return_node = frame.push(func.Return(value_or_stmt=return_value))
|
|
94
|
+
|
|
95
|
+
body = frame.curr_region
|
|
96
|
+
code = func.Function(
|
|
97
|
+
sym_name=kernel_name,
|
|
98
|
+
signature=func.Signature((), return_node.value.type),
|
|
99
|
+
body=body,
|
|
100
|
+
)
|
|
101
|
+
return ir.Method(
|
|
102
|
+
mod=None,
|
|
103
|
+
py_func=None,
|
|
104
|
+
sym_name=kernel_name,
|
|
105
|
+
arg_names=[],
|
|
106
|
+
dialects=kstim.dialects,
|
|
107
|
+
code=code,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def loadfile(file: str | pathlib.Path):
|
|
112
|
+
with open(file) as f:
|
|
113
|
+
return loads(f.read())
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass
|
|
117
|
+
class Stim(lowering.LoweringABC[Node]):
|
|
118
|
+
max_lines: int = field(default=3, kw_only=True)
|
|
119
|
+
hint_indent: int = field(default=2, kw_only=True)
|
|
120
|
+
hint_show_lineno: bool = field(default=True, kw_only=True)
|
|
121
|
+
stacktrace: bool = field(default=True, kw_only=True)
|
|
122
|
+
nonstim_noise_ops: dict[str, kirin.ir.Statement] = field(
|
|
123
|
+
default_factory=dict, kw_only=True
|
|
124
|
+
)
|
|
125
|
+
ignore_unknown_stim: bool = field(default=False, kw_only=True)
|
|
126
|
+
error_unknown_nonstim: bool = field(default=False, kw_only=True)
|
|
127
|
+
|
|
128
|
+
def run(
|
|
129
|
+
self,
|
|
130
|
+
stmt: Node,
|
|
131
|
+
*,
|
|
132
|
+
source: str | None = None,
|
|
133
|
+
globals: dict[str, Any] | None = None,
|
|
134
|
+
file: str | None = None,
|
|
135
|
+
lineno_offset: int = 0,
|
|
136
|
+
col_offset: int = 0,
|
|
137
|
+
compactify: bool = True,
|
|
138
|
+
) -> ir.Region:
|
|
139
|
+
|
|
140
|
+
frame = self.get_frame(
|
|
141
|
+
stmt,
|
|
142
|
+
source=source,
|
|
143
|
+
globals=globals,
|
|
144
|
+
file=file,
|
|
145
|
+
lineno_offset=lineno_offset,
|
|
146
|
+
col_offset=col_offset,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return frame.curr_region
|
|
150
|
+
|
|
151
|
+
def get_frame(
|
|
152
|
+
self,
|
|
153
|
+
stmt: Node,
|
|
154
|
+
source: str | None = None,
|
|
155
|
+
globals: dict[str, Any] | None = None,
|
|
156
|
+
file: str | None = None,
|
|
157
|
+
lineno_offset: int = 0,
|
|
158
|
+
col_offset: int = 0,
|
|
159
|
+
compactify: bool = True,
|
|
160
|
+
) -> lowering.Frame:
|
|
161
|
+
state = lowering.State(
|
|
162
|
+
self,
|
|
163
|
+
file=file,
|
|
164
|
+
lineno_offset=lineno_offset,
|
|
165
|
+
col_offset=col_offset,
|
|
166
|
+
)
|
|
167
|
+
with state.frame(
|
|
168
|
+
[stmt],
|
|
169
|
+
globals=globals,
|
|
170
|
+
finalize_next=False,
|
|
171
|
+
) as frame:
|
|
172
|
+
self.visit(state, stmt)
|
|
173
|
+
|
|
174
|
+
if compactify:
|
|
175
|
+
from kirin.rewrite import Walk, CFGCompactify
|
|
176
|
+
|
|
177
|
+
Walk(CFGCompactify()).rewrite(frame.curr_region)
|
|
178
|
+
|
|
179
|
+
return frame
|
|
180
|
+
|
|
181
|
+
def lower_literal(
|
|
182
|
+
self, state: lowering.State[Node], value: LiteralType
|
|
183
|
+
) -> ir.SSAValue:
|
|
184
|
+
match value:
|
|
185
|
+
case bool():
|
|
186
|
+
stmt = auxiliary.ConstBool(value=value)
|
|
187
|
+
case int():
|
|
188
|
+
stmt = auxiliary.ConstInt(value=value)
|
|
189
|
+
case float():
|
|
190
|
+
stmt = auxiliary.ConstFloat(value=value)
|
|
191
|
+
case str():
|
|
192
|
+
stmt = auxiliary.ConstStr(value=value)
|
|
193
|
+
case _:
|
|
194
|
+
raise lowering.BuildError(
|
|
195
|
+
f"Expected value of type float or int, got {type(value)}."
|
|
196
|
+
)
|
|
197
|
+
state.current_frame.push(stmt)
|
|
198
|
+
return stmt.result
|
|
199
|
+
|
|
200
|
+
def lower_global(
|
|
201
|
+
self,
|
|
202
|
+
state: lowering.State[Node],
|
|
203
|
+
node: Node,
|
|
204
|
+
) -> lowering.LoweringABC.Result:
|
|
205
|
+
raise lowering.BuildError("Global variables are not supported in stim")
|
|
206
|
+
|
|
207
|
+
def visit(self, state: lowering.State[Node], node: Node) -> lowering.Result:
|
|
208
|
+
import stim # Optional dependency required to lower stim circuits
|
|
209
|
+
|
|
210
|
+
match node:
|
|
211
|
+
case stim.Circuit() as circ:
|
|
212
|
+
for inst in circ:
|
|
213
|
+
state.lower(inst)
|
|
214
|
+
case stim.CircuitInstruction() as inst:
|
|
215
|
+
return self.visit_CircuitInstruction(state, node)
|
|
216
|
+
case _:
|
|
217
|
+
raise lowering.BuildError(
|
|
218
|
+
f"Unexpected stim node: {type(node)} ({node!r})"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def _get_qubit_ssa(self, state: lowering.State[Node], target: Node):
|
|
222
|
+
assert target.is_qubit_target, "expect qubit target"
|
|
223
|
+
return self.lower_literal(state, target.qubit_value)
|
|
224
|
+
|
|
225
|
+
def _get_rec_ssa(self, state: lowering.State[Node], node: Node, target: Node):
|
|
226
|
+
assert target.is_measurement_record_target, "expect measurement record target"
|
|
227
|
+
lit = self.lower_literal(state, target.value)
|
|
228
|
+
stmt = auxiliary.GetRecord(id=lit)
|
|
229
|
+
state.current_frame.push(stmt)
|
|
230
|
+
return stmt.result
|
|
231
|
+
|
|
232
|
+
def _get_pauli_string_ssa(self, state: lowering.State[Node], ps_target: list[Node]):
|
|
233
|
+
basis_ssa_list = []
|
|
234
|
+
flipped_ssa_list = []
|
|
235
|
+
tgts_ssa_list = []
|
|
236
|
+
|
|
237
|
+
for targ in ps_target:
|
|
238
|
+
if targ.is_x_target:
|
|
239
|
+
basis_ssa = self.lower_literal(state, "x")
|
|
240
|
+
elif targ.is_y_target:
|
|
241
|
+
basis_ssa = self.lower_literal(state, "y")
|
|
242
|
+
elif targ.is_z_target:
|
|
243
|
+
basis_ssa = self.lower_literal(state, "z")
|
|
244
|
+
|
|
245
|
+
flip_ssa = self.lower_literal(state, targ.is_inverted_result_target)
|
|
246
|
+
targ_ssa = self.lower_literal(state, targ.qubit_value)
|
|
247
|
+
|
|
248
|
+
basis_ssa_list.append(basis_ssa)
|
|
249
|
+
flipped_ssa_list.append(flip_ssa)
|
|
250
|
+
tgts_ssa_list.append(targ_ssa)
|
|
251
|
+
|
|
252
|
+
stmt = auxiliary.NewPauliString(
|
|
253
|
+
string=tuple(basis_ssa_list),
|
|
254
|
+
flipped=tuple(flipped_ssa_list),
|
|
255
|
+
targets=tuple(tgts_ssa_list),
|
|
256
|
+
)
|
|
257
|
+
state.current_frame.push(stmt)
|
|
258
|
+
return stmt.result
|
|
259
|
+
|
|
260
|
+
def _get_multiple_qubit_or_rec_ssa(
|
|
261
|
+
self, state: lowering.State[Node], node: Node, targets: list[Node]
|
|
262
|
+
):
|
|
263
|
+
return tuple(
|
|
264
|
+
(
|
|
265
|
+
self._get_qubit_ssa(state, targ)
|
|
266
|
+
if targ.is_qubit_target
|
|
267
|
+
else self._get_rec_ssa(state, node, targ)
|
|
268
|
+
)
|
|
269
|
+
for targ in targets
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def _get_pauli_string_targets_ssa(
|
|
273
|
+
self, state: lowering.State[Node], node: Node, targets: list[Node]
|
|
274
|
+
):
|
|
275
|
+
ps_list = []
|
|
276
|
+
tmp = []
|
|
277
|
+
prev_combine = True
|
|
278
|
+
for targ in targets:
|
|
279
|
+
if prev_combine is False and targ.is_combiner is False:
|
|
280
|
+
ps_list.append(tmp)
|
|
281
|
+
tmp = [targ]
|
|
282
|
+
else:
|
|
283
|
+
if not targ.is_combiner:
|
|
284
|
+
tmp.append(targ)
|
|
285
|
+
prev_combine = not prev_combine
|
|
286
|
+
|
|
287
|
+
ps_list.append(tmp)
|
|
288
|
+
|
|
289
|
+
return tuple(self._get_pauli_string_ssa(state, ps) for ps in ps_list)
|
|
290
|
+
|
|
291
|
+
def _get_float_args_ssa(
|
|
292
|
+
self, state: lowering.State[Node], gate_args: list[LiteralType]
|
|
293
|
+
):
|
|
294
|
+
return tuple(self.lower_literal(state, val) for val in gate_args)
|
|
295
|
+
|
|
296
|
+
def _get_optional_float_arg_ssa(
|
|
297
|
+
self, state: lowering.State[Node], gate_args: list[LiteralType]
|
|
298
|
+
):
|
|
299
|
+
val = float(gate_args[0]) if len(gate_args) >= 1 else 0.0
|
|
300
|
+
return self.lower_literal(state, val)
|
|
301
|
+
|
|
302
|
+
def _get_optional_int_arg_ssa(
|
|
303
|
+
self, state: lowering.State[Node], gate_args: list[LiteralType]
|
|
304
|
+
):
|
|
305
|
+
val = int(gate_args[0]) if len(gate_args) >= 1 else 0
|
|
306
|
+
return self.lower_literal(state, val)
|
|
307
|
+
|
|
308
|
+
# Stim gates defined here: https://github.com/quantumlib/Stim/blob/main/doc/gates.md
|
|
309
|
+
# collapse-------------------------:
|
|
310
|
+
def _visit_reset(
|
|
311
|
+
self, state: lowering.State[Node], name: str, node
|
|
312
|
+
) -> ir.Statement:
|
|
313
|
+
return getattr(collapse, name)(
|
|
314
|
+
targets=self._get_multiple_qubit_or_rec_ssa(
|
|
315
|
+
state, node, node.targets_copy()
|
|
316
|
+
)
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
def visit_RZ(
|
|
320
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
321
|
+
) -> ir.Statement:
|
|
322
|
+
return self._visit_reset(state, "RZ", node)
|
|
323
|
+
|
|
324
|
+
def visit_RX(
|
|
325
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
326
|
+
) -> ir.Statement:
|
|
327
|
+
return self._visit_reset(state, "RX", node)
|
|
328
|
+
|
|
329
|
+
def visit_RY(
|
|
330
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
331
|
+
) -> ir.Statement:
|
|
332
|
+
return self._visit_reset(state, "RY", node)
|
|
333
|
+
|
|
334
|
+
def _visit_measure(
|
|
335
|
+
self, state: lowering.State[Node], name: str, node
|
|
336
|
+
) -> ir.Statement:
|
|
337
|
+
return getattr(collapse, name)(
|
|
338
|
+
p=self._get_optional_float_arg_ssa(state, node.gate_args_copy()),
|
|
339
|
+
targets=self._get_multiple_qubit_or_rec_ssa(
|
|
340
|
+
state, node, node.targets_copy()
|
|
341
|
+
),
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
def visit_MX(
|
|
345
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
346
|
+
) -> ir.Statement:
|
|
347
|
+
return self._visit_measure(state, "MX", node)
|
|
348
|
+
|
|
349
|
+
def visit_MY(
|
|
350
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
351
|
+
) -> ir.Statement:
|
|
352
|
+
return self._visit_measure(state, "MY", node)
|
|
353
|
+
|
|
354
|
+
def visit_MZ(
|
|
355
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
356
|
+
) -> ir.Statement:
|
|
357
|
+
return self._visit_measure(state, "MZ", node)
|
|
358
|
+
|
|
359
|
+
def visit_MXX(
|
|
360
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
361
|
+
) -> ir.Statement:
|
|
362
|
+
return self._visit_measure(state, "MXX", node)
|
|
363
|
+
|
|
364
|
+
def visit_MYY(
|
|
365
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
366
|
+
) -> ir.Statement:
|
|
367
|
+
return self._visit_measure(state, "MYY", node)
|
|
368
|
+
|
|
369
|
+
def visit_MZZ(
|
|
370
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
371
|
+
) -> ir.Statement:
|
|
372
|
+
return self._visit_measure(state, "MZZ", node)
|
|
373
|
+
|
|
374
|
+
def visit_MPP(
|
|
375
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
376
|
+
) -> ir.Statement:
|
|
377
|
+
return collapse.PPMeasurement(
|
|
378
|
+
p=self._get_optional_float_arg_ssa(state, node.gate_args_copy()),
|
|
379
|
+
targets=self._get_pauli_string_targets_ssa(
|
|
380
|
+
state, node, node.targets_copy()
|
|
381
|
+
),
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# aux.annotate-------------------------:
|
|
385
|
+
def visit_TICK(
|
|
386
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
387
|
+
) -> ir.Statement:
|
|
388
|
+
return auxiliary.Tick()
|
|
389
|
+
|
|
390
|
+
def visit_DETECTOR(
|
|
391
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
392
|
+
) -> ir.Statement:
|
|
393
|
+
return auxiliary.Detector(
|
|
394
|
+
coord=self._get_float_args_ssa(state, node.gate_args_copy()),
|
|
395
|
+
targets=self._get_multiple_qubit_or_rec_ssa(
|
|
396
|
+
state, node, node.targets_copy()
|
|
397
|
+
),
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
def visit_OBSERVABLE_INCLUDE(
|
|
401
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
402
|
+
) -> ir.Statement:
|
|
403
|
+
return auxiliary.ObservableInclude(
|
|
404
|
+
idx=self._get_optional_int_arg_ssa(state, node.gate_args_copy()),
|
|
405
|
+
targets=self._get_multiple_qubit_or_rec_ssa(
|
|
406
|
+
state, node, node.targets_copy()
|
|
407
|
+
),
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
def visit_QUBIT_COORDS(
|
|
411
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
412
|
+
) -> ir.Statement:
|
|
413
|
+
return auxiliary.QubitCoordinates(
|
|
414
|
+
coord=self._get_float_args_ssa(state, node.gate_args_copy()),
|
|
415
|
+
target=self._get_multiple_qubit_or_rec_ssa(
|
|
416
|
+
state, node, node.targets_copy()
|
|
417
|
+
)[0],
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
# gate: Clifford-------------------------:
|
|
421
|
+
# NOTE, we don't need SQRT_Z and SQRT_Z_DAG because stim recognize it as alias of S
|
|
422
|
+
def _visit_clifford(
|
|
423
|
+
self, state: lowering.State[Node], name: str, node
|
|
424
|
+
) -> ir.Statement:
|
|
425
|
+
if "DAG" in name:
|
|
426
|
+
inst_name = name.rstrip("_DAG")
|
|
427
|
+
dagger = True
|
|
428
|
+
else:
|
|
429
|
+
inst_name = name
|
|
430
|
+
dagger = False
|
|
431
|
+
|
|
432
|
+
return getattr(gate, inst_name)(
|
|
433
|
+
targets=self._get_multiple_qubit_or_rec_ssa(
|
|
434
|
+
state, node, node.targets_copy()
|
|
435
|
+
),
|
|
436
|
+
dagger=dagger,
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
def visit_X(
|
|
440
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
441
|
+
) -> ir.Statement:
|
|
442
|
+
return self._visit_clifford(state, "X", node)
|
|
443
|
+
|
|
444
|
+
def visit_Y(
|
|
445
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
446
|
+
) -> ir.Statement:
|
|
447
|
+
return self._visit_clifford(state, "Y", node)
|
|
448
|
+
|
|
449
|
+
def visit_Z(
|
|
450
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
451
|
+
) -> ir.Statement:
|
|
452
|
+
return self._visit_clifford(state, "Z", node)
|
|
453
|
+
|
|
454
|
+
def visit_I(
|
|
455
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
456
|
+
) -> ir.Statement:
|
|
457
|
+
return self._visit_clifford(state, "Identity", node)
|
|
458
|
+
|
|
459
|
+
def visit_H(
|
|
460
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
461
|
+
) -> ir.Statement:
|
|
462
|
+
return self._visit_clifford(state, "H", node)
|
|
463
|
+
|
|
464
|
+
def visit_S(
|
|
465
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
466
|
+
) -> ir.Statement:
|
|
467
|
+
return self._visit_clifford(state, "S", node)
|
|
468
|
+
|
|
469
|
+
def visit_S_DAG(
|
|
470
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
471
|
+
) -> ir.Statement:
|
|
472
|
+
return self._visit_clifford(state, "S_DAG", node)
|
|
473
|
+
|
|
474
|
+
def visit_SQRT_X(
|
|
475
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
476
|
+
) -> ir.Statement:
|
|
477
|
+
return self._visit_clifford(state, "SqrtX", node)
|
|
478
|
+
|
|
479
|
+
def visit_SQRT_Y(
|
|
480
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
481
|
+
) -> ir.Statement:
|
|
482
|
+
return self._visit_clifford(state, "SqrtY", node)
|
|
483
|
+
|
|
484
|
+
def visit_SQRT_X_DAG(
|
|
485
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
486
|
+
) -> ir.Statement:
|
|
487
|
+
return self._visit_clifford(state, "SqrtX_DAG", node)
|
|
488
|
+
|
|
489
|
+
def visit_SQRT_Y_DAG(
|
|
490
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
491
|
+
) -> ir.Statement:
|
|
492
|
+
return self._visit_clifford(state, "SqrtY_DAG", node)
|
|
493
|
+
|
|
494
|
+
def visit_SWAP(
|
|
495
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
496
|
+
) -> ir.Statement:
|
|
497
|
+
return self._visit_clifford(state, "Swap", node)
|
|
498
|
+
|
|
499
|
+
# gate: 2Q gate-------------------------:
|
|
500
|
+
def _visit_2q_gate(
|
|
501
|
+
self, state: lowering.State[Node], name: str, node
|
|
502
|
+
) -> ir.Statement:
|
|
503
|
+
all_targets = self._get_multiple_qubit_or_rec_ssa(
|
|
504
|
+
state, node, node.targets_copy()
|
|
505
|
+
)
|
|
506
|
+
return getattr(gate, name)(
|
|
507
|
+
controls=all_targets[::2],
|
|
508
|
+
targets=all_targets[1::2],
|
|
509
|
+
dagger=False,
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
def visit_CX(
|
|
513
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
514
|
+
) -> ir.Statement:
|
|
515
|
+
return self._visit_2q_gate(state, "CX", node)
|
|
516
|
+
|
|
517
|
+
def visit_CY(
|
|
518
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
519
|
+
) -> ir.Statement:
|
|
520
|
+
return self._visit_2q_gate(state, "CY", node)
|
|
521
|
+
|
|
522
|
+
def visit_CZ(
|
|
523
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
524
|
+
) -> ir.Statement:
|
|
525
|
+
return self._visit_2q_gate(state, "CZ", node)
|
|
526
|
+
|
|
527
|
+
# gate: SPP-------------------------:
|
|
528
|
+
def visit_SPP(
|
|
529
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
530
|
+
) -> ir.Statement:
|
|
531
|
+
return getattr(gate, "SPP")(
|
|
532
|
+
targets=self._get_pauli_string_targets_ssa(
|
|
533
|
+
state, node, node.targets_copy()
|
|
534
|
+
),
|
|
535
|
+
dagger=False,
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
def visit_SPP_DAG(
|
|
539
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
540
|
+
) -> ir.Statement:
|
|
541
|
+
return getattr(gate, "SPP")(
|
|
542
|
+
targets=self._get_pauli_string_targets_ssa(
|
|
543
|
+
state, node, node.targets_copy()
|
|
544
|
+
),
|
|
545
|
+
dagger=True,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
# noise: ..........................................:
|
|
549
|
+
def _visit_single_p_error(
|
|
550
|
+
self, state: lowering.State[Node], name: str, node
|
|
551
|
+
) -> ir.Statement:
|
|
552
|
+
return getattr(noise, name)(
|
|
553
|
+
p=self._get_optional_float_arg_ssa(state, node.gate_args_copy()),
|
|
554
|
+
targets=self._get_multiple_qubit_or_rec_ssa(
|
|
555
|
+
state, node, node.targets_copy()
|
|
556
|
+
),
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
def visit_X_ERROR(
|
|
560
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
561
|
+
) -> ir.Statement:
|
|
562
|
+
return self._visit_single_p_error(state, "XError", node)
|
|
563
|
+
|
|
564
|
+
def visit_Y_ERROR(
|
|
565
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
566
|
+
) -> ir.Statement:
|
|
567
|
+
return self._visit_single_p_error(state, "YError", node)
|
|
568
|
+
|
|
569
|
+
def visit_Z_ERROR(
|
|
570
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
571
|
+
) -> ir.Statement:
|
|
572
|
+
return self._visit_single_p_error(state, "ZError", node)
|
|
573
|
+
|
|
574
|
+
def visit_DEPOLARIZE1(
|
|
575
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
576
|
+
) -> ir.Statement:
|
|
577
|
+
return self._visit_single_p_error(state, "Depolarize1", node)
|
|
578
|
+
|
|
579
|
+
def visit_DEPOLARIZE2(
|
|
580
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
581
|
+
) -> ir.Statement:
|
|
582
|
+
return self._visit_single_p_error(state, "Depolarize2", node)
|
|
583
|
+
|
|
584
|
+
# noise pauli channel 1 & 2............................:
|
|
585
|
+
def visit_PAULI_CHANNEL_1(
|
|
586
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
587
|
+
) -> ir.Statement:
|
|
588
|
+
args = self._get_float_args_ssa(state, node.gate_args_copy())
|
|
589
|
+
return getattr(noise, "PauliChannel1")(
|
|
590
|
+
px=args[0],
|
|
591
|
+
py=args[1],
|
|
592
|
+
pz=args[2],
|
|
593
|
+
targets=self._get_multiple_qubit_or_rec_ssa(
|
|
594
|
+
state, node, node.targets_copy()
|
|
595
|
+
),
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
def visit_PAULI_CHANNEL_2(
|
|
599
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
600
|
+
) -> ir.Statement:
|
|
601
|
+
args = self._get_float_args_ssa(state, node.gate_args_copy())
|
|
602
|
+
return getattr(noise, "PauliChannel2")(
|
|
603
|
+
pix=args[0],
|
|
604
|
+
piy=args[1],
|
|
605
|
+
piz=args[2],
|
|
606
|
+
pxi=args[3],
|
|
607
|
+
pxx=args[4],
|
|
608
|
+
pxy=args[5],
|
|
609
|
+
pxz=args[6],
|
|
610
|
+
pyi=args[7],
|
|
611
|
+
pyx=args[8],
|
|
612
|
+
pyy=args[9],
|
|
613
|
+
pyz=args[10],
|
|
614
|
+
pzi=args[11],
|
|
615
|
+
pzx=args[12],
|
|
616
|
+
pzy=args[13],
|
|
617
|
+
pzz=args[14],
|
|
618
|
+
targets=self._get_multiple_qubit_or_rec_ssa(
|
|
619
|
+
state, node, node.targets_copy()
|
|
620
|
+
),
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
def visit_I_ERROR(
|
|
624
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
625
|
+
) -> ir.Statement | None:
|
|
626
|
+
# I_ERROR represents any noise supported by external simulators but not stim
|
|
627
|
+
# Parse tag
|
|
628
|
+
tag_parts = node.tag.split(";", maxsplit=1)[0].split(":", maxsplit=1)
|
|
629
|
+
nonstim_name = tag_parts[0]
|
|
630
|
+
nonce = 0
|
|
631
|
+
if len(tag_parts) == 2:
|
|
632
|
+
try:
|
|
633
|
+
nonce = int(tag_parts[1])
|
|
634
|
+
except ValueError:
|
|
635
|
+
# String was not an integer
|
|
636
|
+
if self.error_unknown_nonstim:
|
|
637
|
+
raise lowering.BuildError(
|
|
638
|
+
f"Unsupported non-stim tag format: {node.tag!r} ({node!r})"
|
|
639
|
+
)
|
|
640
|
+
return
|
|
641
|
+
if nonstim_name not in self.nonstim_noise_ops and self.error_unknown_nonstim:
|
|
642
|
+
raise lowering.BuildError(
|
|
643
|
+
f"Unknown non-stim statement name: {nonstim_name!r} ({node!r})"
|
|
644
|
+
)
|
|
645
|
+
statement_cls = self.nonstim_noise_ops.get(nonstim_name)
|
|
646
|
+
if statement_cls is not None:
|
|
647
|
+
if issubclass(statement_cls, noise.NonStimCorrelatedError):
|
|
648
|
+
stmt = statement_cls(
|
|
649
|
+
nonce=nonce,
|
|
650
|
+
probs=self._get_float_args_ssa(state, node.gate_args_copy()),
|
|
651
|
+
targets=self._get_multiple_qubit_or_rec_ssa(
|
|
652
|
+
state, node, node.targets_copy()
|
|
653
|
+
),
|
|
654
|
+
)
|
|
655
|
+
else:
|
|
656
|
+
stmt = statement_cls(
|
|
657
|
+
probs=self._get_float_args_ssa(state, node.gate_args_copy()),
|
|
658
|
+
targets=self._get_multiple_qubit_or_rec_ssa(
|
|
659
|
+
state, node, node.targets_copy()
|
|
660
|
+
),
|
|
661
|
+
)
|
|
662
|
+
return stmt
|
|
663
|
+
|
|
664
|
+
def visit_CircuitInstruction(
|
|
665
|
+
self, state: lowering.State[Node], node: "stim.CircuitInstruction"
|
|
666
|
+
) -> lowering.Result:
|
|
667
|
+
name = node.name.upper()
|
|
668
|
+
|
|
669
|
+
match name:
|
|
670
|
+
# Stim name abbreviation substitutions to canonical name
|
|
671
|
+
case "R":
|
|
672
|
+
name = "RZ"
|
|
673
|
+
case "M":
|
|
674
|
+
name = "MZ"
|
|
675
|
+
|
|
676
|
+
# dispatch base on name (capital)
|
|
677
|
+
inst = getattr(self, f"visit_{name}", None)
|
|
678
|
+
if inst is not None:
|
|
679
|
+
stmt = inst(state, node)
|
|
680
|
+
if stmt is not None:
|
|
681
|
+
state.current_frame.push(stmt)
|
|
682
|
+
else:
|
|
683
|
+
if not self.ignore_unknown_stim:
|
|
684
|
+
raise lowering.BuildError(
|
|
685
|
+
f"Unsupported stim instruction: {type(node)} ({node!r})"
|
|
686
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bloqade-circuit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: The software development toolkit for neutral atom arrays.
|
|
5
5
|
Author-email: Roger-luo <rluo@quera.com>, kaihsin <khwu@quera.com>, weinbe58 <pweinberg@quera.com>, johnzl-777 <jlong@quera.com>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -9,8 +9,8 @@ Requires-Dist: kirin-toolchain~=0.17.0
|
|
|
9
9
|
Requires-Dist: numpy>=1.22.0
|
|
10
10
|
Requires-Dist: pandas>=2.2.3
|
|
11
11
|
Requires-Dist: pydantic<2.11.0,>=1.3.0
|
|
12
|
-
Requires-Dist: pyqrack-cpu
|
|
13
|
-
Requires-Dist: pyqrack
|
|
12
|
+
Requires-Dist: pyqrack-cpu<1.41,>=1.38.2; sys_platform != 'darwin'
|
|
13
|
+
Requires-Dist: pyqrack<1.41,>=1.38.2; sys_platform == 'darwin'
|
|
14
14
|
Requires-Dist: rich>=13.9.4
|
|
15
15
|
Requires-Dist: scipy>=1.13.1
|
|
16
16
|
Provides-Extra: cirq
|
|
@@ -24,6 +24,8 @@ Provides-Extra: qasm2
|
|
|
24
24
|
Requires-Dist: lark>=1.2.2; extra == 'qasm2'
|
|
25
25
|
Provides-Extra: qbraid
|
|
26
26
|
Requires-Dist: qbraid>=0.9.3; extra == 'qbraid'
|
|
27
|
+
Provides-Extra: stim
|
|
28
|
+
Requires-Dist: stim>=1.15.0; extra == 'stim'
|
|
27
29
|
Provides-Extra: vis
|
|
28
30
|
Requires-Dist: ffmpeg>=1.4; extra == 'vis'
|
|
29
31
|
Requires-Dist: matplotlib>=3.9.2; extra == 'vis'
|