bloqade-circuit 0.6.4__py3-none-any.whl → 0.9.1__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.
Files changed (191) hide show
  1. bloqade/analysis/address/__init__.py +8 -4
  2. bloqade/analysis/address/analysis.py +123 -33
  3. bloqade/analysis/address/impls.py +293 -90
  4. bloqade/analysis/address/lattice.py +209 -24
  5. bloqade/analysis/fidelity/analysis.py +11 -23
  6. bloqade/analysis/measure_id/analysis.py +18 -20
  7. bloqade/analysis/measure_id/impls.py +31 -29
  8. bloqade/annotate/__init__.py +6 -0
  9. bloqade/annotate/_dialect.py +3 -0
  10. bloqade/annotate/_interface.py +22 -0
  11. bloqade/annotate/stmts.py +29 -0
  12. bloqade/annotate/types.py +13 -0
  13. bloqade/cirq_utils/__init__.py +4 -2
  14. bloqade/cirq_utils/emit/__init__.py +3 -0
  15. bloqade/cirq_utils/emit/base.py +246 -0
  16. bloqade/cirq_utils/emit/gate.py +104 -0
  17. bloqade/cirq_utils/emit/noise.py +90 -0
  18. bloqade/cirq_utils/emit/qubit.py +35 -0
  19. bloqade/cirq_utils/lowering.py +660 -0
  20. bloqade/cirq_utils/noise/__init__.py +0 -2
  21. bloqade/cirq_utils/noise/_two_zone_utils.py +7 -15
  22. bloqade/cirq_utils/noise/model.py +151 -191
  23. bloqade/cirq_utils/noise/transform.py +2 -2
  24. bloqade/cirq_utils/parallelize.py +9 -6
  25. bloqade/gemini/__init__.py +1 -0
  26. bloqade/gemini/analysis/__init__.py +3 -0
  27. bloqade/gemini/analysis/logical_validation/__init__.py +1 -0
  28. bloqade/gemini/analysis/logical_validation/analysis.py +17 -0
  29. bloqade/gemini/analysis/logical_validation/impls.py +101 -0
  30. bloqade/gemini/groups.py +67 -0
  31. bloqade/native/__init__.py +23 -0
  32. bloqade/native/_prelude.py +45 -0
  33. bloqade/native/dialects/__init__.py +0 -0
  34. bloqade/native/dialects/gate/__init__.py +2 -0
  35. bloqade/native/dialects/gate/_dialect.py +3 -0
  36. bloqade/native/dialects/gate/_interface.py +32 -0
  37. bloqade/native/dialects/gate/stmts.py +31 -0
  38. bloqade/native/stdlib/__init__.py +0 -0
  39. bloqade/native/stdlib/broadcast.py +246 -0
  40. bloqade/native/stdlib/simple.py +220 -0
  41. bloqade/native/upstream/__init__.py +4 -0
  42. bloqade/native/upstream/squin2native.py +79 -0
  43. bloqade/pyqrack/__init__.py +2 -2
  44. bloqade/pyqrack/base.py +7 -1
  45. bloqade/pyqrack/device.py +192 -18
  46. bloqade/pyqrack/native.py +49 -0
  47. bloqade/pyqrack/reg.py +6 -6
  48. bloqade/pyqrack/squin/gate/__init__.py +1 -0
  49. bloqade/pyqrack/squin/gate/gate.py +136 -0
  50. bloqade/pyqrack/squin/noise/native.py +120 -54
  51. bloqade/pyqrack/squin/qubit.py +39 -36
  52. bloqade/pyqrack/target.py +5 -4
  53. bloqade/pyqrack/task.py +114 -7
  54. bloqade/qasm2/_qasm_loading.py +3 -3
  55. bloqade/qasm2/dialects/core/address.py +21 -12
  56. bloqade/qasm2/dialects/expr/_emit.py +19 -8
  57. bloqade/qasm2/dialects/expr/stmts.py +7 -7
  58. bloqade/qasm2/dialects/noise/fidelity.py +4 -8
  59. bloqade/qasm2/dialects/noise/model.py +2 -1
  60. bloqade/qasm2/emit/base.py +16 -11
  61. bloqade/qasm2/emit/gate.py +11 -8
  62. bloqade/qasm2/emit/main.py +103 -3
  63. bloqade/qasm2/emit/target.py +9 -5
  64. bloqade/qasm2/groups.py +3 -2
  65. bloqade/qasm2/parse/lowering.py +0 -1
  66. bloqade/qasm2/passes/fold.py +14 -73
  67. bloqade/qasm2/passes/glob.py +2 -2
  68. bloqade/qasm2/passes/noise.py +1 -1
  69. bloqade/qasm2/passes/parallel.py +7 -5
  70. bloqade/qasm2/rewrite/__init__.py +0 -1
  71. bloqade/qasm2/rewrite/noise/heuristic_noise.py +7 -17
  72. bloqade/qasm2/rewrite/parallel_to_glob.py +28 -15
  73. bloqade/qasm2/rewrite/parallel_to_uop.py +2 -8
  74. bloqade/qasm2/rewrite/register.py +2 -2
  75. bloqade/qasm2/rewrite/uop_to_parallel.py +4 -2
  76. bloqade/qbraid/lowering.py +1 -0
  77. bloqade/qbraid/schema.py +2 -2
  78. bloqade/qubit/__init__.py +12 -0
  79. bloqade/qubit/_dialect.py +3 -0
  80. bloqade/qubit/_interface.py +49 -0
  81. bloqade/qubit/_prelude.py +45 -0
  82. bloqade/qubit/analysis/__init__.py +1 -0
  83. bloqade/qubit/analysis/address_impl.py +40 -0
  84. bloqade/qubit/stdlib/__init__.py +2 -0
  85. bloqade/qubit/stdlib/_new.py +34 -0
  86. bloqade/qubit/stdlib/broadcast.py +62 -0
  87. bloqade/qubit/stdlib/simple.py +59 -0
  88. bloqade/qubit/stmts.py +60 -0
  89. bloqade/rewrite/passes/__init__.py +6 -0
  90. bloqade/rewrite/passes/aggressive_unroll.py +103 -0
  91. bloqade/rewrite/passes/callgraph.py +116 -0
  92. bloqade/rewrite/passes/canonicalize_ilist.py +20 -14
  93. bloqade/rewrite/rules/split_ifs.py +18 -1
  94. bloqade/squin/__init__.py +47 -14
  95. bloqade/squin/analysis/__init__.py +0 -1
  96. bloqade/squin/analysis/schedule.py +10 -11
  97. bloqade/squin/gate/__init__.py +2 -0
  98. bloqade/squin/gate/_dialect.py +3 -0
  99. bloqade/squin/gate/_interface.py +98 -0
  100. bloqade/squin/gate/stmts.py +125 -0
  101. bloqade/squin/groups.py +5 -22
  102. bloqade/squin/noise/__init__.py +1 -10
  103. bloqade/squin/noise/_dialect.py +1 -1
  104. bloqade/squin/noise/_interface.py +45 -0
  105. bloqade/squin/noise/stmts.py +66 -28
  106. bloqade/squin/rewrite/U3_to_clifford.py +70 -51
  107. bloqade/squin/rewrite/__init__.py +0 -2
  108. bloqade/squin/rewrite/remove_dangling_qubits.py +2 -2
  109. bloqade/squin/rewrite/wrap_analysis.py +4 -35
  110. bloqade/squin/stdlib/__init__.py +0 -0
  111. bloqade/squin/stdlib/broadcast/__init__.py +34 -0
  112. bloqade/squin/stdlib/broadcast/_qubit.py +4 -0
  113. bloqade/squin/stdlib/broadcast/gate.py +260 -0
  114. bloqade/squin/stdlib/broadcast/noise.py +144 -0
  115. bloqade/squin/stdlib/simple/__init__.py +33 -0
  116. bloqade/squin/stdlib/simple/gate.py +242 -0
  117. bloqade/squin/stdlib/simple/noise.py +126 -0
  118. bloqade/stim/__init__.py +1 -0
  119. bloqade/stim/_wrappers.py +6 -0
  120. bloqade/stim/dialects/auxiliary/emit.py +19 -18
  121. bloqade/stim/dialects/collapse/emit_str.py +7 -8
  122. bloqade/stim/dialects/gate/emit.py +9 -10
  123. bloqade/stim/dialects/noise/emit.py +17 -13
  124. bloqade/stim/dialects/noise/stmts.py +5 -3
  125. bloqade/stim/emit/__init__.py +1 -0
  126. bloqade/stim/emit/impls.py +16 -0
  127. bloqade/stim/emit/stim_str.py +48 -31
  128. bloqade/stim/groups.py +12 -2
  129. bloqade/stim/parse/lowering.py +14 -17
  130. bloqade/stim/passes/__init__.py +0 -2
  131. bloqade/stim/passes/flatten.py +26 -0
  132. bloqade/stim/passes/simplify_ifs.py +6 -1
  133. bloqade/stim/passes/squin_to_stim.py +9 -84
  134. bloqade/stim/rewrite/__init__.py +2 -4
  135. bloqade/stim/rewrite/get_record_util.py +24 -0
  136. bloqade/stim/rewrite/ifs_to_stim.py +24 -25
  137. bloqade/stim/rewrite/qubit_to_stim.py +90 -41
  138. bloqade/stim/rewrite/set_detector_to_stim.py +68 -0
  139. bloqade/stim/rewrite/set_observable_to_stim.py +52 -0
  140. bloqade/stim/rewrite/squin_measure.py +9 -18
  141. bloqade/stim/rewrite/squin_noise.py +134 -108
  142. bloqade/stim/rewrite/util.py +5 -192
  143. bloqade/test_utils.py +1 -1
  144. bloqade/types.py +10 -0
  145. bloqade/validation/__init__.py +2 -0
  146. bloqade/validation/analysis/__init__.py +5 -0
  147. bloqade/validation/analysis/analysis.py +41 -0
  148. bloqade/validation/analysis/lattice.py +58 -0
  149. bloqade/validation/kernel_validation.py +77 -0
  150. {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/METADATA +5 -6
  151. bloqade_circuit-0.9.1.dist-info/RECORD +265 -0
  152. bloqade/pyqrack/squin/op.py +0 -180
  153. bloqade/pyqrack/squin/runtime.py +0 -535
  154. bloqade/pyqrack/squin/wire.py +0 -51
  155. bloqade/rewrite/rules/flatten_ilist.py +0 -51
  156. bloqade/rewrite/rules/inline_getitem_ilist.py +0 -31
  157. bloqade/squin/_typeinfer.py +0 -20
  158. bloqade/squin/analysis/address_impl.py +0 -71
  159. bloqade/squin/analysis/nsites/__init__.py +0 -9
  160. bloqade/squin/analysis/nsites/analysis.py +0 -50
  161. bloqade/squin/analysis/nsites/impls.py +0 -92
  162. bloqade/squin/analysis/nsites/lattice.py +0 -49
  163. bloqade/squin/cirq/__init__.py +0 -280
  164. bloqade/squin/cirq/emit/emit_circuit.py +0 -109
  165. bloqade/squin/cirq/emit/noise.py +0 -49
  166. bloqade/squin/cirq/emit/op.py +0 -125
  167. bloqade/squin/cirq/emit/qubit.py +0 -60
  168. bloqade/squin/cirq/emit/runtime.py +0 -242
  169. bloqade/squin/cirq/lowering.py +0 -440
  170. bloqade/squin/lowering.py +0 -54
  171. bloqade/squin/noise/_wrapper.py +0 -40
  172. bloqade/squin/noise/rewrite.py +0 -111
  173. bloqade/squin/op/__init__.py +0 -41
  174. bloqade/squin/op/_dialect.py +0 -3
  175. bloqade/squin/op/_wrapper.py +0 -121
  176. bloqade/squin/op/number.py +0 -5
  177. bloqade/squin/op/rewrite.py +0 -46
  178. bloqade/squin/op/stdlib.py +0 -62
  179. bloqade/squin/op/stmts.py +0 -276
  180. bloqade/squin/op/traits.py +0 -43
  181. bloqade/squin/op/types.py +0 -26
  182. bloqade/squin/qubit.py +0 -184
  183. bloqade/squin/rewrite/canonicalize.py +0 -60
  184. bloqade/squin/rewrite/desugar.py +0 -124
  185. bloqade/squin/types.py +0 -8
  186. bloqade/squin/wire.py +0 -201
  187. bloqade/stim/rewrite/wire_identity_elimination.py +0 -24
  188. bloqade/stim/rewrite/wire_to_stim.py +0 -57
  189. bloqade_circuit-0.6.4.dist-info/RECORD +0 -234
  190. {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/WHEEL +0 -0
  191. {bloqade_circuit-0.6.4.dist-info → bloqade_circuit-0.9.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,535 +0,0 @@
1
- from typing import Any
2
- from dataclasses import field, dataclass
3
-
4
- import numpy as np
5
- from kirin.dialects import ilist
6
-
7
- from pyqrack.pauli import Pauli
8
- from bloqade.pyqrack import PyQrackQubit
9
-
10
-
11
- @dataclass(frozen=True)
12
- class OperatorRuntimeABC:
13
- """The number of sites the operator applies to (including controls)"""
14
-
15
- @property
16
- def n_sites(self) -> int: ...
17
-
18
- def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
19
- raise NotImplementedError(
20
- "Operator runtime base class should not be called directly, override the method"
21
- )
22
-
23
- def control_apply(
24
- self,
25
- controls: tuple[PyQrackQubit, ...],
26
- targets: tuple[PyQrackQubit, ...],
27
- adjoint: bool = False,
28
- ) -> None:
29
- raise RuntimeError(f"Can't apply controlled version of {self}")
30
-
31
- def broadcast_apply(self, qubits: ilist.IList[PyQrackQubit, Any], **kwargs) -> None:
32
- n = self.n_sites
33
-
34
- if len(qubits) % n != 0:
35
- raise RuntimeError(
36
- f"Cannot broadcast operator {self} that applies to {n} over {len(qubits)} qubits."
37
- )
38
-
39
- for qubit_index in range(0, len(qubits), n):
40
- targets = qubits[qubit_index : qubit_index + n]
41
- self.apply(*targets, **kwargs)
42
-
43
-
44
- @dataclass(frozen=True)
45
- class ResetRuntime(OperatorRuntimeABC):
46
- """Reset the qubit to the target state"""
47
-
48
- target_state: bool
49
-
50
- @property
51
- def n_sites(self) -> int:
52
- return 1
53
-
54
- def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
55
- for qubit in qubits:
56
- if not qubit.is_active():
57
- continue
58
-
59
- res: bool = qubit.sim_reg.m(qubit.addr)
60
- if res != self.target_state:
61
- qubit.sim_reg.x(qubit.addr)
62
-
63
-
64
- @dataclass(frozen=True)
65
- class OperatorRuntime(OperatorRuntimeABC):
66
- method_name: str
67
-
68
- @property
69
- def n_sites(self) -> int:
70
- return 1
71
-
72
- def get_method_name(self, adjoint: bool, control: bool) -> str:
73
- method_name = ""
74
- if control:
75
- method_name += "mc"
76
-
77
- if adjoint and self.method_name in ("s", "t"):
78
- method_name += "adj"
79
-
80
- return method_name + self.method_name
81
-
82
- def apply(self, qubit: PyQrackQubit, adjoint: bool = False) -> None:
83
- if not qubit.is_active():
84
- return
85
- method_name = self.get_method_name(adjoint=adjoint, control=False)
86
- getattr(qubit.sim_reg, method_name)(qubit.addr)
87
-
88
- def control_apply(
89
- self,
90
- controls: tuple[PyQrackQubit, ...],
91
- targets: tuple[PyQrackQubit],
92
- adjoint: bool = False,
93
- ) -> None:
94
- target = targets[0]
95
- if not target.is_active():
96
- return
97
-
98
- ctrls: list[int] = []
99
- for qbit in controls:
100
- if not qbit.is_active():
101
- return
102
-
103
- ctrls.append(qbit.addr)
104
-
105
- method_name = self.get_method_name(adjoint=adjoint, control=True)
106
- getattr(target.sim_reg, method_name)(ctrls, target.addr)
107
-
108
-
109
- @dataclass(frozen=True)
110
- class ControlRuntime(OperatorRuntimeABC):
111
- op: OperatorRuntimeABC
112
- n_controls: int
113
-
114
- @property
115
- def n_sites(self) -> int:
116
- return self.op.n_sites + self.n_controls
117
-
118
- def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
119
- ctrls = qubits[: self.n_controls]
120
- targets = qubits[self.n_controls :]
121
-
122
- if len(targets) != self.op.n_sites:
123
- raise RuntimeError(
124
- f"Cannot apply operator {self.op} to {len(targets)} qubits! It applies to {self.op.n_sites}, check your inputs!"
125
- )
126
-
127
- self.op.control_apply(controls=ctrls, targets=targets, adjoint=adjoint)
128
-
129
-
130
- @dataclass(frozen=True)
131
- class ProjectorRuntime(OperatorRuntimeABC):
132
- to_state: bool
133
-
134
- @property
135
- def n_sites(self) -> int:
136
- return 1
137
-
138
- def apply(self, qubit: PyQrackQubit, adjoint: bool = False) -> None:
139
- if not qubit.is_active():
140
- return
141
- qubit.sim_reg.force_m(qubit.addr, self.to_state)
142
-
143
- def control_apply(
144
- self,
145
- controls: tuple[PyQrackQubit, ...],
146
- targets: tuple[PyQrackQubit],
147
- adjoint: bool = False,
148
- ) -> None:
149
- target = targets[0]
150
- if not target.is_active():
151
- return
152
-
153
- ctrls: list[int] = []
154
- for qbit in controls:
155
- if not qbit.is_active():
156
- return
157
-
158
- m = [not self.to_state, 0, 0, self.to_state]
159
- target.sim_reg.mcmtrx(ctrls, m, target.addr)
160
-
161
-
162
- @dataclass(frozen=True)
163
- class IdentityRuntime(OperatorRuntimeABC):
164
- # TODO: do we even need sites? The apply never does anything
165
- sites: int
166
-
167
- @property
168
- def n_sites(self) -> int:
169
- return self.sites
170
-
171
- def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
172
- pass
173
-
174
- def control_apply(
175
- self,
176
- controls: tuple[PyQrackQubit, ...],
177
- targets: tuple[PyQrackQubit, ...],
178
- adjoint: bool = False,
179
- ) -> None:
180
- pass
181
-
182
-
183
- @dataclass(frozen=True)
184
- class MultRuntime(OperatorRuntimeABC):
185
- lhs: OperatorRuntimeABC
186
- rhs: OperatorRuntimeABC
187
-
188
- @property
189
- def n_sites(self) -> int:
190
- if self.lhs.n_sites != self.rhs.n_sites:
191
- raise RuntimeError("Multiplication of operators with unequal size.")
192
-
193
- return self.lhs.n_sites
194
-
195
- def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
196
- if adjoint:
197
- # NOTE: inverted order
198
- self.lhs.apply(*qubits, adjoint=adjoint)
199
- self.rhs.apply(*qubits, adjoint=adjoint)
200
- else:
201
- self.rhs.apply(*qubits)
202
- self.lhs.apply(*qubits)
203
-
204
- def control_apply(
205
- self,
206
- controls: tuple[PyQrackQubit, ...],
207
- targets: tuple[PyQrackQubit, ...],
208
- adjoint: bool = False,
209
- ) -> None:
210
- if adjoint:
211
- self.lhs.control_apply(controls=controls, targets=targets, adjoint=adjoint)
212
- self.rhs.control_apply(controls=controls, targets=targets, adjoint=adjoint)
213
- else:
214
- self.rhs.control_apply(controls=controls, targets=targets, adjoint=adjoint)
215
- self.lhs.control_apply(controls=controls, targets=targets, adjoint=adjoint)
216
-
217
-
218
- @dataclass(frozen=True)
219
- class KronRuntime(OperatorRuntimeABC):
220
- lhs: OperatorRuntimeABC
221
- rhs: OperatorRuntimeABC
222
-
223
- @property
224
- def n_sites(self) -> int:
225
- return self.lhs.n_sites + self.rhs.n_sites
226
-
227
- def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
228
- self.lhs.apply(*qubits[: self.lhs.n_sites], adjoint=adjoint)
229
- self.rhs.apply(*qubits[self.lhs.n_sites :], adjoint=adjoint)
230
-
231
- def control_apply(
232
- self,
233
- controls: tuple[PyQrackQubit, ...],
234
- targets: tuple[PyQrackQubit, ...],
235
- adjoint: bool = False,
236
- ) -> None:
237
- self.lhs.control_apply(
238
- controls=controls,
239
- targets=tuple(targets[: self.lhs.n_sites]),
240
- adjoint=adjoint,
241
- )
242
- self.rhs.control_apply(
243
- controls=controls,
244
- targets=tuple(targets[self.lhs.n_sites :]),
245
- adjoint=adjoint,
246
- )
247
-
248
-
249
- @dataclass(frozen=True)
250
- class ScaleRuntime(OperatorRuntimeABC):
251
- op: OperatorRuntimeABC
252
- factor: complex
253
-
254
- @property
255
- def n_sites(self) -> int:
256
- return self.op.n_sites
257
-
258
- @staticmethod
259
- def mat(factor, adjoint: bool):
260
- if adjoint:
261
- return [np.conj(factor), 0, 0, factor]
262
- else:
263
- return [factor, 0, 0, factor]
264
-
265
- def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
266
- self.op.apply(*qubits, adjoint=adjoint)
267
-
268
- # NOTE: when applying to multiple qubits, we "spread" the factor evenly
269
- applied_factor = self.factor ** (1.0 / len(qubits))
270
- for qbit in qubits:
271
- if not qbit.is_active():
272
- continue
273
-
274
- # NOTE: just factor * eye(2)
275
- m = self.mat(applied_factor, adjoint)
276
-
277
- # TODO: output seems to always be normalized -- no-op?
278
- qbit.sim_reg.mtrx(m, qbit.addr)
279
-
280
- def control_apply(
281
- self,
282
- controls: tuple[PyQrackQubit, ...],
283
- targets: tuple[PyQrackQubit, ...],
284
- adjoint: bool = False,
285
- ) -> None:
286
-
287
- ctrls: list[int] = []
288
- for qbit in controls:
289
- if not qbit.is_active():
290
- return
291
-
292
- ctrls.append(qbit.addr)
293
-
294
- self.op.control_apply(controls=controls, targets=targets, adjoint=adjoint)
295
-
296
- applied_factor = self.factor ** (1.0 / len(targets))
297
- for target in targets:
298
- m = self.mat(applied_factor, adjoint=adjoint)
299
- target.sim_reg.mcmtrx(ctrls, m, target.addr)
300
-
301
-
302
- @dataclass(frozen=True)
303
- class MtrxOpRuntime(OperatorRuntimeABC):
304
- def mat(self, adjoint: bool) -> list[complex]:
305
- raise NotImplementedError("Override this method in the subclass!")
306
-
307
- @property
308
- def n_sites(self) -> int:
309
- # NOTE: pyqrack only supports 2x2 matrices, i.e. single qubit applications
310
- return 1
311
-
312
- def apply(self, target: PyQrackQubit, adjoint: bool = False) -> None:
313
- if not target.is_active():
314
- return
315
-
316
- m = self.mat(adjoint=adjoint)
317
- target.sim_reg.mtrx(m, target.addr)
318
-
319
- def control_apply(
320
- self,
321
- controls: tuple[PyQrackQubit, ...],
322
- targets: tuple[PyQrackQubit, ...],
323
- adjoint: bool = False,
324
- ) -> None:
325
- target = targets[0]
326
- if not target.is_active():
327
- return
328
-
329
- ctrls: list[int] = []
330
- for qbit in controls:
331
- if not qbit.is_active():
332
- return
333
-
334
- ctrls.append(qbit.addr)
335
-
336
- m = self.mat(adjoint=adjoint)
337
- target.sim_reg.mcmtrx(ctrls, m, target.addr)
338
-
339
-
340
- @dataclass(frozen=True)
341
- class SpRuntime(MtrxOpRuntime):
342
- def mat(self, adjoint: bool) -> list[complex]:
343
- if adjoint:
344
- return [0, 0, 0.5, 0]
345
- else:
346
- return [0, 0.5, 0, 0]
347
-
348
-
349
- @dataclass(frozen=True)
350
- class SnRuntime(MtrxOpRuntime):
351
- def mat(self, adjoint: bool) -> list[complex]:
352
- if adjoint:
353
- return [0, 0.5, 0, 0]
354
- else:
355
- return [0, 0, 0.5, 0]
356
-
357
-
358
- @dataclass(frozen=True)
359
- class PhaseOpRuntime(MtrxOpRuntime):
360
- theta: float
361
- global_: bool
362
-
363
- def mat(self, adjoint: bool) -> list[complex]:
364
- sign = (-1) ** (not adjoint)
365
- local_phase = np.exp(sign * 1j * self.theta)
366
-
367
- # NOTE: this is just 1 if we want a local shift
368
- global_phase = np.exp(sign * 1j * self.theta * self.global_)
369
-
370
- return [global_phase, 0, 0, local_phase]
371
-
372
-
373
- @dataclass(frozen=True)
374
- class RotRuntime(OperatorRuntimeABC):
375
- axis: OperatorRuntimeABC
376
- angle: float
377
- pyqrack_axis: Pauli = field(init=False)
378
-
379
- @property
380
- def n_sites(self) -> int:
381
- return 1
382
-
383
- def __post_init__(self):
384
- if not isinstance(self.axis, OperatorRuntime):
385
- raise RuntimeError(
386
- f"Rotation only supported for Pauli operators! Got {self.axis}"
387
- )
388
-
389
- try:
390
- axis = getattr(Pauli, "Pauli" + self.axis.method_name.upper())
391
- except KeyError:
392
- raise RuntimeError(
393
- f"Rotation only supported for Pauli operators! Got {self.axis}"
394
- )
395
-
396
- # NOTE: weird setattr for frozen dataclasses
397
- object.__setattr__(self, "pyqrack_axis", axis)
398
-
399
- def apply(self, target: PyQrackQubit, adjoint: bool = False) -> None:
400
- if not target.is_active():
401
- return
402
-
403
- sign = (-1) ** adjoint
404
- angle = sign * self.angle
405
- target.sim_reg.r(self.pyqrack_axis, angle, target.addr)
406
-
407
- def control_apply(
408
- self,
409
- controls: tuple[PyQrackQubit, ...],
410
- targets: tuple[PyQrackQubit, ...],
411
- adjoint: bool = False,
412
- ) -> None:
413
- target = targets[0]
414
- if not target.is_active():
415
- return
416
-
417
- ctrls: list[int] = []
418
- for qbit in controls:
419
- if not qbit.is_active():
420
- return
421
-
422
- ctrls.append(qbit.addr)
423
-
424
- sign = (-1) ** (not adjoint)
425
- angle = sign * self.angle
426
- target.sim_reg.mcr(self.pyqrack_axis, angle, ctrls, target.addr)
427
-
428
-
429
- @dataclass(frozen=True)
430
- class AdjointRuntime(OperatorRuntimeABC):
431
- op: OperatorRuntimeABC
432
-
433
- @property
434
- def n_sites(self) -> int:
435
- return self.op.n_sites
436
-
437
- def apply(self, *qubits: PyQrackQubit, adjoint: bool = False) -> None:
438
- # NOTE: to account for adjoint(adjoint(op))
439
- passed_on_adjoint = not adjoint
440
-
441
- self.op.apply(*qubits, adjoint=passed_on_adjoint)
442
-
443
- def control_apply(
444
- self,
445
- controls: tuple[PyQrackQubit, ...],
446
- targets: tuple[PyQrackQubit, ...],
447
- adjoint: bool = False,
448
- ) -> None:
449
- passed_on_adjoint = not adjoint
450
- self.op.control_apply(
451
- controls=controls, targets=targets, adjoint=passed_on_adjoint
452
- )
453
-
454
-
455
- @dataclass(frozen=True)
456
- class U3Runtime(OperatorRuntimeABC):
457
- theta: float
458
- phi: float
459
- lam: float
460
-
461
- @property
462
- def n_sites(self) -> int:
463
- return 1
464
-
465
- def angles(self, adjoint: bool) -> tuple[float, float, float]:
466
- if adjoint:
467
- # NOTE: adjoint(U(theta, phi, lam)) == U(-theta, -lam, -phi)
468
- return -self.theta, -self.lam, -self.phi
469
- else:
470
- return self.theta, self.phi, self.lam
471
-
472
- def apply(self, target: PyQrackQubit, adjoint: bool = False) -> None:
473
- if not target.is_active():
474
- return
475
-
476
- angles = self.angles(adjoint=adjoint)
477
- target.sim_reg.u(target.addr, *angles)
478
-
479
- def control_apply(
480
- self,
481
- controls: tuple[PyQrackQubit, ...],
482
- targets: tuple[PyQrackQubit, ...],
483
- adjoint: bool = False,
484
- ) -> None:
485
- target = targets[0]
486
- if not target.is_active():
487
- return
488
-
489
- ctrls: list[int] = []
490
- for qbit in controls:
491
- if not qbit.is_active():
492
- return
493
-
494
- ctrls.append(qbit.addr)
495
-
496
- angles = self.angles(adjoint=adjoint)
497
- target.sim_reg.mcu(ctrls, target.addr, *angles)
498
-
499
-
500
- @dataclass(frozen=True)
501
- class PauliStringRuntime(OperatorRuntimeABC):
502
- string: str
503
- ops: list[OperatorRuntime]
504
-
505
- @property
506
- def n_sites(self) -> int:
507
- return sum((op.n_sites for op in self.ops))
508
-
509
- def apply(self, *qubits: PyQrackQubit, adjoint: bool = False):
510
- if len(qubits) != self.n_sites:
511
- raise RuntimeError(
512
- f"Cannot apply Pauli string {self.string} to {len(qubits)} qubits! Make sure the number of qubits matches."
513
- )
514
-
515
- qubit_index = 0
516
- for op in self.ops:
517
- next_qubit_index = qubit_index + op.n_sites
518
- op.apply(*qubits[qubit_index:next_qubit_index], adjoint=adjoint)
519
- qubit_index = next_qubit_index
520
-
521
- def control_apply(
522
- self,
523
- controls: tuple[PyQrackQubit, ...],
524
- targets: tuple[PyQrackQubit, ...],
525
- adjoint: bool = False,
526
- ) -> None:
527
- if len(targets) != self.n_sites:
528
- raise RuntimeError(
529
- f"Cannot apply Pauli string {self.string} to {len(targets)} qubits! Make sure the number of qubits matches."
530
- )
531
-
532
- for i, op in enumerate(self.ops):
533
- # NOTE: this is fine as the size of each op is actually just 1 by definition
534
- target = targets[i]
535
- op.control_apply(controls=controls, targets=(target,))
@@ -1,51 +0,0 @@
1
- from kirin import interp
2
-
3
- from bloqade.squin import wire
4
- from bloqade.pyqrack.reg import PyQrackWire, PyQrackQubit
5
- from bloqade.pyqrack.base import PyQrackInterpreter
6
-
7
- from .runtime import OperatorRuntimeABC
8
-
9
-
10
- @wire.dialect.register(key="pyqrack")
11
- class PyQrackMethods(interp.MethodTable):
12
- # @interp.impl(wire.Wrap)
13
- # def wrap(self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Wrap):
14
- # traits = frozenset({lowering.FromPythonCall(), WireTerminator()})
15
- # wire: ir.SSAValue = info.argument(WireType)
16
- # qubit: ir.SSAValue = info.argument(QubitType)
17
-
18
- @interp.impl(wire.Unwrap)
19
- def unwrap(
20
- self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Unwrap
21
- ):
22
- q: PyQrackQubit = frame.get(stmt.qubit)
23
- return (PyQrackWire(q),)
24
-
25
- @interp.impl(wire.Apply)
26
- def apply(self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Apply):
27
- ws = stmt.inputs
28
- assert isinstance(ws, tuple)
29
- qubits: list[PyQrackQubit] = []
30
- for w in ws:
31
- assert isinstance(w, PyQrackWire)
32
- qubits.append(w.qubit)
33
- op: OperatorRuntimeABC = frame.get(stmt.operator)
34
-
35
- op.apply(*qubits)
36
-
37
- out_ws = [PyQrackWire(qbit) for qbit in qubits]
38
- return (out_ws,)
39
-
40
- @interp.impl(wire.Measure)
41
- def measure(
42
- self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Measure
43
- ):
44
- w: PyQrackWire = frame.get(stmt.wire)
45
- qbit = w.qubit
46
-
47
- if not qbit.is_active():
48
- return (interp.loss_m_result,)
49
-
50
- res: bool = bool(qbit.sim_reg.m(qbit.addr))
51
- return (res,)
@@ -1,51 +0,0 @@
1
- from dataclasses import dataclass
2
-
3
- from kirin import ir
4
- from kirin.dialects import py, ilist
5
- from kirin.rewrite.abc import RewriteRule, RewriteResult
6
-
7
-
8
- @dataclass
9
- class FlattenAddOpIList(RewriteRule):
10
-
11
- def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
12
- if not isinstance(node, py.binop.Add):
13
- return RewriteResult()
14
-
15
- # check if we are adding two ilist.New objects
16
- new_data = ()
17
-
18
- # lhs:
19
- if not isinstance(node.lhs.owner, ilist.New):
20
- if not (
21
- isinstance(node.lhs.owner, py.Constant)
22
- and isinstance(
23
- const_ilist := node.lhs.owner.value.unwrap(), ilist.IList
24
- )
25
- and len(const_ilist.data) == 0
26
- ):
27
- return RewriteResult()
28
-
29
- else:
30
- new_data += node.lhs.owner.values
31
-
32
- # rhs:
33
- if not isinstance(node.rhs.owner, ilist.New):
34
- if not (
35
- isinstance(node.rhs.owner, py.Constant)
36
- and isinstance(
37
- const_ilist := node.rhs.owner.value.unwrap(), ilist.IList
38
- )
39
- and len(const_ilist.data) == 0
40
- ):
41
- return RewriteResult()
42
-
43
- else:
44
- new_data += node.rhs.owner.values
45
-
46
- new_stmt = ilist.New(values=new_data)
47
- node.replace_by(new_stmt)
48
-
49
- return RewriteResult(
50
- has_done_something=True,
51
- )
@@ -1,31 +0,0 @@
1
- from dataclasses import dataclass
2
-
3
- from kirin import ir
4
- from kirin.analysis import const
5
- from kirin.dialects import py, ilist
6
- from kirin.rewrite.abc import RewriteRule, RewriteResult
7
-
8
-
9
- @dataclass
10
- class InlineGetItemFromIList(RewriteRule):
11
- constprop_result: dict[ir.SSAValue, const.Result]
12
-
13
- def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
14
- if not isinstance(node, py.indexing.GetItem):
15
- return RewriteResult()
16
-
17
- if not isinstance(node.obj.owner, ilist.New):
18
- return RewriteResult()
19
-
20
- if not isinstance(
21
- index_value := self.constprop_result.get(node.index), const.Value
22
- ):
23
- return RewriteResult()
24
-
25
- elem_ssa = node.obj.owner.values[index_value.data]
26
-
27
- node.result.replace_by(elem_ssa)
28
-
29
- return RewriteResult(
30
- has_done_something=True,
31
- )
@@ -1,20 +0,0 @@
1
- from kirin import types, interp
2
- from kirin.analysis import TypeInference, const
3
- from kirin.dialects import ilist
4
-
5
- from bloqade import squin
6
-
7
-
8
- @squin.qubit.dialect.register(key="typeinfer")
9
- class TypeInfer(interp.MethodTable):
10
- @interp.impl(squin.qubit.New)
11
- def _call(self, interp: TypeInference, frame: interp.Frame, stmt: squin.qubit.New):
12
- # based on Xiu-zhe (Roger) Luo's get_const_value function
13
-
14
- if (hint := stmt.n_qubits.hints.get("const")) is None:
15
- return (ilist.IListType[squin.qubit.QubitType, types.Any],)
16
-
17
- if isinstance(hint, const.Value) and isinstance(hint.data, int):
18
- return (ilist.IListType[squin.qubit.QubitType, types.Literal(hint.data)],)
19
-
20
- return (ilist.IListType[squin.qubit.QubitType, types.Any],)