bloqade-circuit 0.1.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/__init__.py +0 -0
- bloqade/analysis/address/__init__.py +11 -0
- bloqade/analysis/address/analysis.py +60 -0
- bloqade/analysis/address/impls.py +228 -0
- bloqade/analysis/address/lattice.py +85 -0
- bloqade/noise/__init__.py +1 -0
- bloqade/noise/native/__init__.py +20 -0
- bloqade/noise/native/_dialect.py +3 -0
- bloqade/noise/native/_wrappers.py +34 -0
- bloqade/noise/native/model.py +347 -0
- bloqade/noise/native/rewrite.py +35 -0
- bloqade/noise/native/stmts.py +46 -0
- bloqade/pyqrack/__init__.py +18 -0
- bloqade/pyqrack/base.py +131 -0
- bloqade/pyqrack/noise/__init__.py +0 -0
- bloqade/pyqrack/noise/native.py +100 -0
- bloqade/pyqrack/qasm2/__init__.py +0 -0
- bloqade/pyqrack/qasm2/core.py +79 -0
- bloqade/pyqrack/qasm2/parallel.py +46 -0
- bloqade/pyqrack/qasm2/uop.py +247 -0
- bloqade/pyqrack/reg.py +109 -0
- bloqade/pyqrack/target.py +112 -0
- bloqade/qasm2/__init__.py +19 -0
- bloqade/qasm2/_wrappers.py +674 -0
- bloqade/qasm2/dialects/__init__.py +10 -0
- bloqade/qasm2/dialects/core/__init__.py +3 -0
- bloqade/qasm2/dialects/core/_dialect.py +3 -0
- bloqade/qasm2/dialects/core/_emit.py +68 -0
- bloqade/qasm2/dialects/core/_typeinfer.py +23 -0
- bloqade/qasm2/dialects/core/address.py +38 -0
- bloqade/qasm2/dialects/core/stmts.py +94 -0
- bloqade/qasm2/dialects/expr/__init__.py +3 -0
- bloqade/qasm2/dialects/expr/_dialect.py +3 -0
- bloqade/qasm2/dialects/expr/_emit.py +103 -0
- bloqade/qasm2/dialects/expr/_from_python.py +86 -0
- bloqade/qasm2/dialects/expr/_interp.py +75 -0
- bloqade/qasm2/dialects/expr/stmts.py +262 -0
- bloqade/qasm2/dialects/glob.py +45 -0
- bloqade/qasm2/dialects/indexing.py +64 -0
- bloqade/qasm2/dialects/inline.py +76 -0
- bloqade/qasm2/dialects/noise.py +16 -0
- bloqade/qasm2/dialects/parallel.py +110 -0
- bloqade/qasm2/dialects/uop/__init__.py +4 -0
- bloqade/qasm2/dialects/uop/_dialect.py +3 -0
- bloqade/qasm2/dialects/uop/_emit.py +211 -0
- bloqade/qasm2/dialects/uop/schedule.py +89 -0
- bloqade/qasm2/dialects/uop/stmts.py +325 -0
- bloqade/qasm2/emit/__init__.py +1 -0
- bloqade/qasm2/emit/base.py +72 -0
- bloqade/qasm2/emit/gate.py +102 -0
- bloqade/qasm2/emit/main.py +106 -0
- bloqade/qasm2/emit/target.py +165 -0
- bloqade/qasm2/glob.py +24 -0
- bloqade/qasm2/groups.py +120 -0
- bloqade/qasm2/parallel.py +48 -0
- bloqade/qasm2/parse/__init__.py +37 -0
- bloqade/qasm2/parse/ast.py +235 -0
- bloqade/qasm2/parse/build.py +289 -0
- bloqade/qasm2/parse/lowering.py +553 -0
- bloqade/qasm2/parse/parser.py +5 -0
- bloqade/qasm2/parse/print.py +293 -0
- bloqade/qasm2/parse/qasm2.lark +75 -0
- bloqade/qasm2/parse/visitor.py +16 -0
- bloqade/qasm2/parse/visitor.pyi +39 -0
- bloqade/qasm2/passes/__init__.py +5 -0
- bloqade/qasm2/passes/fold.py +94 -0
- bloqade/qasm2/passes/glob.py +119 -0
- bloqade/qasm2/passes/noise.py +61 -0
- bloqade/qasm2/passes/parallel.py +176 -0
- bloqade/qasm2/passes/py2qasm.py +63 -0
- bloqade/qasm2/passes/qasm2py.py +61 -0
- bloqade/qasm2/rewrite/__init__.py +12 -0
- bloqade/qasm2/rewrite/desugar.py +28 -0
- bloqade/qasm2/rewrite/glob.py +103 -0
- bloqade/qasm2/rewrite/heuristic_noise.py +247 -0
- bloqade/qasm2/rewrite/native_gates.py +447 -0
- bloqade/qasm2/rewrite/parallel_to_uop.py +83 -0
- bloqade/qasm2/rewrite/register.py +45 -0
- bloqade/qasm2/rewrite/uop_to_parallel.py +395 -0
- bloqade/qasm2/types.py +39 -0
- bloqade/qbraid/__init__.py +2 -0
- bloqade/qbraid/lowering.py +324 -0
- bloqade/qbraid/schema.py +252 -0
- bloqade/qbraid/simulation_result.py +99 -0
- bloqade/qbraid/target.py +86 -0
- bloqade/squin/__init__.py +2 -0
- bloqade/squin/analysis/__init__.py +0 -0
- bloqade/squin/analysis/nsites/__init__.py +8 -0
- bloqade/squin/analysis/nsites/analysis.py +52 -0
- bloqade/squin/analysis/nsites/impls.py +69 -0
- bloqade/squin/analysis/nsites/lattice.py +49 -0
- bloqade/squin/analysis/schedule.py +244 -0
- bloqade/squin/groups.py +38 -0
- bloqade/squin/op/__init__.py +132 -0
- bloqade/squin/op/_dialect.py +3 -0
- bloqade/squin/op/complex.py +6 -0
- bloqade/squin/op/stmts.py +220 -0
- bloqade/squin/op/traits.py +43 -0
- bloqade/squin/op/types.py +10 -0
- bloqade/squin/qubit.py +118 -0
- bloqade/squin/wire.py +103 -0
- bloqade/stim/__init__.py +6 -0
- bloqade/stim/_wrappers.py +186 -0
- bloqade/stim/dialects/__init__.py +5 -0
- bloqade/stim/dialects/aux/__init__.py +11 -0
- bloqade/stim/dialects/aux/_dialect.py +3 -0
- bloqade/stim/dialects/aux/emit.py +102 -0
- bloqade/stim/dialects/aux/interp.py +39 -0
- bloqade/stim/dialects/aux/lowering.py +40 -0
- bloqade/stim/dialects/aux/stmts/__init__.py +14 -0
- bloqade/stim/dialects/aux/stmts/annotate.py +47 -0
- bloqade/stim/dialects/aux/stmts/const.py +95 -0
- bloqade/stim/dialects/aux/types.py +19 -0
- bloqade/stim/dialects/collapse/__init__.py +3 -0
- bloqade/stim/dialects/collapse/_dialect.py +3 -0
- bloqade/stim/dialects/collapse/emit.py +68 -0
- bloqade/stim/dialects/collapse/stmts/__init__.py +3 -0
- bloqade/stim/dialects/collapse/stmts/measure.py +45 -0
- bloqade/stim/dialects/collapse/stmts/pp_measure.py +14 -0
- bloqade/stim/dialects/collapse/stmts/reset.py +26 -0
- bloqade/stim/dialects/gate/__init__.py +3 -0
- bloqade/stim/dialects/gate/_dialect.py +3 -0
- bloqade/stim/dialects/gate/emit.py +87 -0
- bloqade/stim/dialects/gate/stmts/__init__.py +14 -0
- bloqade/stim/dialects/gate/stmts/base.py +31 -0
- bloqade/stim/dialects/gate/stmts/clifford_1q.py +53 -0
- bloqade/stim/dialects/gate/stmts/clifford_2q.py +11 -0
- bloqade/stim/dialects/gate/stmts/control_2q.py +21 -0
- bloqade/stim/dialects/gate/stmts/pp.py +15 -0
- bloqade/stim/dialects/noise/__init__.py +3 -0
- bloqade/stim/dialects/noise/_dialect.py +3 -0
- bloqade/stim/dialects/noise/emit.py +66 -0
- bloqade/stim/dialects/noise/stmts.py +77 -0
- bloqade/stim/emit/__init__.py +1 -0
- bloqade/stim/emit/stim.py +54 -0
- bloqade/stim/groups.py +26 -0
- bloqade/test_utils.py +35 -0
- bloqade/types.py +24 -0
- bloqade/visual/__init__.py +1 -0
- bloqade/visual/animation/__init__.py +0 -0
- bloqade/visual/animation/animate.py +267 -0
- bloqade/visual/animation/base.py +346 -0
- bloqade/visual/animation/gate_event.py +24 -0
- bloqade/visual/animation/runtime/__init__.py +0 -0
- bloqade/visual/animation/runtime/aod.py +36 -0
- bloqade/visual/animation/runtime/atoms.py +55 -0
- bloqade/visual/animation/runtime/ppoly.py +50 -0
- bloqade/visual/animation/runtime/qpustate.py +119 -0
- bloqade/visual/animation/runtime/utils.py +43 -0
- bloqade_circuit-0.1.0.dist-info/METADATA +70 -0
- bloqade_circuit-0.1.0.dist-info/RECORD +153 -0
- bloqade_circuit-0.1.0.dist-info/WHEEL +4 -0
- bloqade_circuit-0.1.0.dist-info/licenses/LICENSE +234 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
import dataclasses
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Any, Dict, List, Tuple, Callable, Optional
|
|
6
|
+
|
|
7
|
+
import matplotlib.patches as mpatches
|
|
8
|
+
import matplotlib.collections as mcollections
|
|
9
|
+
|
|
10
|
+
from .gate_event import GateEvent
|
|
11
|
+
|
|
12
|
+
_gate_color_code = {
|
|
13
|
+
"GlobalCZGate": "tab:red",
|
|
14
|
+
"TopHatCZGate": "tab:red",
|
|
15
|
+
"GlobalHyperfineRotation": "tab:blue",
|
|
16
|
+
"GlobalHyperfineZRotation": "tab:green",
|
|
17
|
+
"LocalHyperfineZRotation": "tab:green",
|
|
18
|
+
"LocalHyperfineZRotationPos": "tab:green",
|
|
19
|
+
"LocalHyperfineRotation": "tab:blue",
|
|
20
|
+
"LocalHyperfineRotationPos": "tab:blue",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class quera_color_code(str, Enum):
|
|
25
|
+
# TODO: for python 3.11+, replace traits with StrEnum
|
|
26
|
+
purple = "#6437FF"
|
|
27
|
+
red = "#C2477F"
|
|
28
|
+
yellow = "#EADD08"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclasses.dataclass
|
|
32
|
+
class GateArtist:
|
|
33
|
+
mpl_ax: Any
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def clear_data(self) -> None:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def get_artists(self) -> Tuple[Any]:
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclasses.dataclass(init=False)
|
|
45
|
+
class GlobalGateArtist(GateArtist):
|
|
46
|
+
mpl_obj: mpatches.Rectangle
|
|
47
|
+
|
|
48
|
+
def __init__(self, mpl_ax: Any, xmin, ymin, width, height, color):
|
|
49
|
+
super().__init__(mpl_ax)
|
|
50
|
+
rc = mpatches.Rectangle(
|
|
51
|
+
[xmin, ymin], width, height, color=color, alpha=0.6, visible=False
|
|
52
|
+
)
|
|
53
|
+
mpl_ax.add_patch(rc)
|
|
54
|
+
self.mpl_obj = rc
|
|
55
|
+
|
|
56
|
+
def clear_data(self) -> None:
|
|
57
|
+
self.mpl_obj.set_width(0)
|
|
58
|
+
self.mpl_obj.set_height(0)
|
|
59
|
+
self.mpl_obj.set_xy([0, 0])
|
|
60
|
+
|
|
61
|
+
def get_artists(self) -> Tuple[Any]:
|
|
62
|
+
return (self.mpl_obj,)
|
|
63
|
+
|
|
64
|
+
def set_visible(self, visible: bool):
|
|
65
|
+
self.mpl_obj.set_visible(visible)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclasses.dataclass(init=False)
|
|
69
|
+
class RowRegionGateArtist(GateArtist):
|
|
70
|
+
"""
|
|
71
|
+
A row region gate artist object.
|
|
72
|
+
|
|
73
|
+
bound box is [y_origin - width/2, y_origin + width/2]
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
mpl_obj: mpatches.Rectangle
|
|
77
|
+
mpl_obj_keepout_top: mpatches.Rectangle
|
|
78
|
+
mpl_obj_keepout_btm: mpatches.Rectangle
|
|
79
|
+
width: float
|
|
80
|
+
xmin: float
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self, mpl_ax: Any, xmin, width, ymin, ymin_keepout, ymax, ymax_keepout, color
|
|
84
|
+
):
|
|
85
|
+
super().__init__(mpl_ax)
|
|
86
|
+
self.width = width
|
|
87
|
+
self.xmin = xmin
|
|
88
|
+
rc_btm = mpatches.Rectangle(
|
|
89
|
+
[xmin, ymin_keepout],
|
|
90
|
+
width,
|
|
91
|
+
ymin - ymin_keepout,
|
|
92
|
+
color=color,
|
|
93
|
+
alpha=0.3,
|
|
94
|
+
visible=False,
|
|
95
|
+
)
|
|
96
|
+
mpl_ax.add_patch(rc_btm)
|
|
97
|
+
self.mpl_obj_keepout_btm = rc_btm
|
|
98
|
+
|
|
99
|
+
rc = mpatches.Rectangle(
|
|
100
|
+
[xmin, ymin], width, ymax - ymin, color=color, alpha=0.6, visible=False
|
|
101
|
+
)
|
|
102
|
+
mpl_ax.add_patch(rc)
|
|
103
|
+
self.mpl_obj = rc
|
|
104
|
+
|
|
105
|
+
rc_top = mpatches.Rectangle(
|
|
106
|
+
[xmin, ymax],
|
|
107
|
+
width,
|
|
108
|
+
ymax_keepout - ymax,
|
|
109
|
+
color=color,
|
|
110
|
+
alpha=0.3,
|
|
111
|
+
visible=False,
|
|
112
|
+
)
|
|
113
|
+
mpl_ax.add_patch(rc_top)
|
|
114
|
+
self.mpl_obj_keepout_top = rc_top
|
|
115
|
+
|
|
116
|
+
def clear_data(self) -> None:
|
|
117
|
+
self.mpl_obj.set_width(0)
|
|
118
|
+
self.mpl_obj.set_height(0)
|
|
119
|
+
self.mpl_obj.set_xy([0, 0])
|
|
120
|
+
|
|
121
|
+
self.mpl_obj_keepout_top.set_width(0)
|
|
122
|
+
self.mpl_obj_keepout_top.set_height(0)
|
|
123
|
+
self.mpl_obj_keepout_top.set_xy([0, 0])
|
|
124
|
+
|
|
125
|
+
self.mpl_obj_keepout_btm.set_width(0)
|
|
126
|
+
self.mpl_obj_keepout_btm.set_height(0)
|
|
127
|
+
self.mpl_obj_keepout_btm.set_xy([0, 0])
|
|
128
|
+
|
|
129
|
+
def get_artists(self) -> Tuple[Any]:
|
|
130
|
+
return (self.mpl_obj, self.mpl_obj_keepout_top, self.mpl_obj_keepout_btm)
|
|
131
|
+
|
|
132
|
+
def update_data(self, ymin, ymax, ymin_keepout, ymax_keepout):
|
|
133
|
+
self.mpl_obj.set_height(ymax - ymin)
|
|
134
|
+
self.mpl_obj.set_width(self.width)
|
|
135
|
+
self.mpl_obj.set_xy([self.xmin, ymin])
|
|
136
|
+
|
|
137
|
+
self.mpl_obj_keepout_top.set_height(ymax_keepout - ymax)
|
|
138
|
+
self.mpl_obj_keepout_top.set_width(self.width)
|
|
139
|
+
self.mpl_obj_keepout_top.set_xy([self.xmin, ymax])
|
|
140
|
+
|
|
141
|
+
self.mpl_obj_keepout_btm.set_height(ymin - ymin_keepout)
|
|
142
|
+
self.mpl_obj_keepout_btm.set_width(self.width)
|
|
143
|
+
self.mpl_obj_keepout_btm.set_xy([self.xmin, ymin_keepout])
|
|
144
|
+
|
|
145
|
+
def set_visible(self, visible: bool):
|
|
146
|
+
self.mpl_obj.set_visible(visible)
|
|
147
|
+
self.mpl_obj_keepout_btm.set_visible(visible)
|
|
148
|
+
self.mpl_obj_keepout_top.set_visible(visible)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclasses.dataclass
|
|
152
|
+
class SpotGateArtist(GateArtist):
|
|
153
|
+
mpl_obj: mcollections.PathCollection
|
|
154
|
+
|
|
155
|
+
def __init__(self, mpl_ax: Any, xy: List[Tuple[float, float]], radius, color):
|
|
156
|
+
super().__init__(mpl_ax)
|
|
157
|
+
self.mpl_obj = mpl_ax.scatter(
|
|
158
|
+
[x for x, y in xy],
|
|
159
|
+
[y for x, y in xy],
|
|
160
|
+
s=radius,
|
|
161
|
+
visible=False,
|
|
162
|
+
marker="o",
|
|
163
|
+
facecolors=color,
|
|
164
|
+
alpha=0.5,
|
|
165
|
+
zorder=+100,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
def clear_data(self) -> None:
|
|
169
|
+
self.mpl_obj.set_offsets([])
|
|
170
|
+
|
|
171
|
+
def get_artists(self) -> Tuple[Any]:
|
|
172
|
+
return (self.mpl_obj,)
|
|
173
|
+
|
|
174
|
+
def update_data(self, xy: List[Tuple[float, float]]):
|
|
175
|
+
self.mpl_obj.set_offsets(xy)
|
|
176
|
+
|
|
177
|
+
def set_visible(self, visible: bool):
|
|
178
|
+
self.mpl_obj.set_visible(visible)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@dataclasses.dataclass
|
|
182
|
+
class FieldOfView:
|
|
183
|
+
xmin: Optional[float] = dataclasses.field(default=None)
|
|
184
|
+
xmax: Optional[float] = dataclasses.field(default=None)
|
|
185
|
+
ymin: Optional[float] = dataclasses.field(default=None)
|
|
186
|
+
ymax: Optional[float] = dataclasses.field(default=None)
|
|
187
|
+
|
|
188
|
+
def not_defined(self):
|
|
189
|
+
return (
|
|
190
|
+
self.xmin is None
|
|
191
|
+
or self.xmax is None
|
|
192
|
+
or self.ymin is None
|
|
193
|
+
or self.ymax is None
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def width(self):
|
|
198
|
+
return self.xmax - self.xmin
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def height(self):
|
|
202
|
+
return self.ymax - self.ymin
|
|
203
|
+
|
|
204
|
+
def to_json(self):
|
|
205
|
+
return {
|
|
206
|
+
"xmin": self.xmin,
|
|
207
|
+
"xmax": self.xmax,
|
|
208
|
+
"ymin": self.ymin,
|
|
209
|
+
"ymax": self.ymax,
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@classmethod
|
|
213
|
+
def from_json(cls, json_dict):
|
|
214
|
+
return FieldOfView(
|
|
215
|
+
xmin=json_dict["xmin"],
|
|
216
|
+
xmax=json_dict["xmax"],
|
|
217
|
+
ymin=json_dict["ymin"],
|
|
218
|
+
ymax=json_dict["ymax"],
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@dataclasses.dataclass(init=False)
|
|
223
|
+
class GatePainter:
|
|
224
|
+
artist_objs: Dict[str, GateArtist]
|
|
225
|
+
artist_methods: Dict[str, Callable]
|
|
226
|
+
|
|
227
|
+
def __init__(self, mpl_ax, qpu_fov: FieldOfView, scale: float = 1.0):
|
|
228
|
+
# gates:
|
|
229
|
+
self.artist_objs = {}
|
|
230
|
+
self.artist_methods = {
|
|
231
|
+
"GlobalCZGate": self.process_global_cz_gate,
|
|
232
|
+
"GlobalHyperfineRotation": self.process_global_hyperfine_rotation,
|
|
233
|
+
"TopHatCZGate": self.process_tophat_cz_gate,
|
|
234
|
+
"LocalHyperfineZRotation": self.process_local_hyperfine_z_rotation,
|
|
235
|
+
"LocalHyperfineZRotationPos": self.process_local_hyperfine_z_rotation_pos,
|
|
236
|
+
"LocalHyperfineRotation": self.process_local_hyperfine_rotation,
|
|
237
|
+
"LocalHyperfineRotationPos": self.process_local_hyperfine_rotation_pos,
|
|
238
|
+
}
|
|
239
|
+
self.artist_objs["GlobalCZGate"] = GlobalGateArtist(
|
|
240
|
+
mpl_ax=mpl_ax,
|
|
241
|
+
xmin=qpu_fov.xmin,
|
|
242
|
+
width=qpu_fov.width,
|
|
243
|
+
ymin=qpu_fov.ymin,
|
|
244
|
+
height=qpu_fov.height,
|
|
245
|
+
color=_gate_color_code["GlobalCZGate"],
|
|
246
|
+
)
|
|
247
|
+
self.artist_objs["GlobalHyperfineRotation"] = GlobalGateArtist(
|
|
248
|
+
mpl_ax=mpl_ax,
|
|
249
|
+
xmin=qpu_fov.xmin,
|
|
250
|
+
width=qpu_fov.width,
|
|
251
|
+
ymin=qpu_fov.ymin,
|
|
252
|
+
height=qpu_fov.height,
|
|
253
|
+
color=_gate_color_code["GlobalHyperfineRotation"],
|
|
254
|
+
)
|
|
255
|
+
self.artist_objs["TopHatCZGate"] = RowRegionGateArtist(
|
|
256
|
+
mpl_ax=mpl_ax,
|
|
257
|
+
xmin=qpu_fov.xmin,
|
|
258
|
+
width=qpu_fov.width,
|
|
259
|
+
ymin=0,
|
|
260
|
+
ymin_keepout=0,
|
|
261
|
+
ymax=0,
|
|
262
|
+
ymax_keepout=0,
|
|
263
|
+
color=_gate_color_code["TopHatCZGate"],
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
self.artist_objs["LocalHyperfineZRotation"] = SpotGateArtist(
|
|
267
|
+
mpl_ax=mpl_ax,
|
|
268
|
+
xy=[],
|
|
269
|
+
radius=160 * scale,
|
|
270
|
+
color=_gate_color_code["LocalHyperfineZRotation"],
|
|
271
|
+
)
|
|
272
|
+
self.artist_objs["LocalHyperfineZRotationPos"] = SpotGateArtist(
|
|
273
|
+
mpl_ax=mpl_ax,
|
|
274
|
+
xy=[],
|
|
275
|
+
radius=160 * scale,
|
|
276
|
+
color=_gate_color_code["LocalHyperfineZRotationPos"],
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
self.artist_objs["LocalHyperfineRotation"] = SpotGateArtist(
|
|
280
|
+
mpl_ax=mpl_ax,
|
|
281
|
+
xy=[],
|
|
282
|
+
radius=80,
|
|
283
|
+
color=_gate_color_code["LocalHyperfineRotation"],
|
|
284
|
+
)
|
|
285
|
+
self.artist_objs["LocalHyperfineRotationPos"] = SpotGateArtist(
|
|
286
|
+
mpl_ax=mpl_ax,
|
|
287
|
+
xy=[],
|
|
288
|
+
radius=80,
|
|
289
|
+
color=_gate_color_code["LocalHyperfineRotationPos"],
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
@staticmethod
|
|
293
|
+
def process_global_cz_gate(artist_obj, **kwargs) -> None:
|
|
294
|
+
artist_obj.set_visible(True)
|
|
295
|
+
|
|
296
|
+
@staticmethod
|
|
297
|
+
def process_global_hyperfine_rotation(artist_obj, **kwargs) -> None:
|
|
298
|
+
artist_obj.set_visible(True)
|
|
299
|
+
|
|
300
|
+
@staticmethod
|
|
301
|
+
def process_tophat_cz_gate(artist_obj, **kwargs) -> None:
|
|
302
|
+
artist_obj.update_data(
|
|
303
|
+
ymin=kwargs["y_min"],
|
|
304
|
+
ymax=kwargs["y_max"],
|
|
305
|
+
ymin_keepout=kwargs["y_min_keepout"],
|
|
306
|
+
ymax_keepout=kwargs["y_max_keepout"],
|
|
307
|
+
)
|
|
308
|
+
artist_obj.set_visible(True)
|
|
309
|
+
|
|
310
|
+
@staticmethod
|
|
311
|
+
def process_local_hyperfine_z_rotation_pos(artist_obj, **kwargs) -> None:
|
|
312
|
+
artist_obj.update_data(xy=[(x, kwargs["y_row_loc"]) for x in kwargs["varargs"]])
|
|
313
|
+
artist_obj.set_visible(True)
|
|
314
|
+
|
|
315
|
+
@staticmethod
|
|
316
|
+
def process_local_hyperfine_rotation_pos(artist_obj, **kwargs) -> None:
|
|
317
|
+
artist_obj.update_data(xy=[(x, kwargs["y_row_loc"]) for x in kwargs["varargs"]])
|
|
318
|
+
artist_obj.set_visible(True)
|
|
319
|
+
|
|
320
|
+
@staticmethod
|
|
321
|
+
def process_local_hyperfine_z_rotation(artist_obj, **kwargs) -> None:
|
|
322
|
+
raise ValueError(
|
|
323
|
+
"cannot have local HyperfineZRotation, it should be already lowered to Pos variant!"
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
@staticmethod
|
|
327
|
+
def process_local_hyperfine_rotation(artist_obj, **kwargs) -> None:
|
|
328
|
+
raise ValueError(
|
|
329
|
+
"cannot have local HyperfineRotation, it should be already lowered to Pos variant!"
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
def process_gates(self, gates: List[GateEvent]) -> List:
|
|
333
|
+
|
|
334
|
+
for gate_artist in self.artist_objs.values():
|
|
335
|
+
gate_artist.set_visible(False)
|
|
336
|
+
|
|
337
|
+
for gate in gates:
|
|
338
|
+
self.artist_methods[gate.cls_name](
|
|
339
|
+
self.artist_objs[gate.cls_name], **gate.kwargs
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
return list(
|
|
343
|
+
itertools.chain.from_iterable(
|
|
344
|
+
gate_artist.get_artists() for gate_artist in self.artist_objs.values()
|
|
345
|
+
)
|
|
346
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclasses.dataclass(frozen=True)
|
|
6
|
+
class GateEvent:
|
|
7
|
+
cls_name: str
|
|
8
|
+
kwargs: Dict[str, Any]
|
|
9
|
+
duration: float
|
|
10
|
+
|
|
11
|
+
def to_json(self) -> Dict[str, Any]:
|
|
12
|
+
return {
|
|
13
|
+
"cls_name": self.cls_name,
|
|
14
|
+
"kwargs": self.kwargs,
|
|
15
|
+
"duration": self.duration,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def from_json(cls, json_dict: Dict[str, Any]) -> "GateEvent":
|
|
20
|
+
return cls(
|
|
21
|
+
json_dict["cls_name"],
|
|
22
|
+
json_dict["kwargs"],
|
|
23
|
+
json_dict["duration"],
|
|
24
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from typing import Any, Dict, Tuple
|
|
3
|
+
|
|
4
|
+
from .ppoly import PPoly
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclasses.dataclass(frozen=True)
|
|
8
|
+
class AODMoveEvent:
|
|
9
|
+
time: float
|
|
10
|
+
duration: float
|
|
11
|
+
x: PPoly
|
|
12
|
+
y: PPoly
|
|
13
|
+
|
|
14
|
+
def __post_init__(self):
|
|
15
|
+
assert isinstance(self.x, PPoly), "x must be a PPoly instance"
|
|
16
|
+
assert isinstance(self.y, PPoly), "y must be a PPoly instance"
|
|
17
|
+
|
|
18
|
+
def sample(self, t: float) -> Tuple[float, float]:
|
|
19
|
+
return (self.x(t - self.time), self.y(t - self.time))
|
|
20
|
+
|
|
21
|
+
def to_json(self) -> Dict[str, Any]:
|
|
22
|
+
return {
|
|
23
|
+
"time": self.time,
|
|
24
|
+
"duration": self.duration,
|
|
25
|
+
"x": self.x.to_json(),
|
|
26
|
+
"y": self.y.to_json(),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def from_json(cls, json_dict: Dict[str, Any]) -> "AODMoveEvent":
|
|
31
|
+
return cls(
|
|
32
|
+
json_dict["time"],
|
|
33
|
+
json_dict["duration"],
|
|
34
|
+
PPoly.from_json(json_dict["x"]),
|
|
35
|
+
PPoly.from_json(json_dict["y"]),
|
|
36
|
+
)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from .ppoly import PPoly
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclasses.dataclass(frozen=True)
|
|
11
|
+
class AtomTrajectory:
|
|
12
|
+
id: int
|
|
13
|
+
x: PPoly
|
|
14
|
+
y: PPoly
|
|
15
|
+
events: List[Tuple[float, str]] # (time, event_type)
|
|
16
|
+
|
|
17
|
+
def __post_init__(self):
|
|
18
|
+
assert all(
|
|
19
|
+
isinstance(event, tuple) and len(event) == 2 for event in self.events
|
|
20
|
+
), "All events must be (float, str) pairs"
|
|
21
|
+
assert isinstance(self.id, int), "id must be an int"
|
|
22
|
+
assert isinstance(self.x, PPoly), "x must be a PPoly instance"
|
|
23
|
+
assert isinstance(self.y, PPoly), "y must be a PPoly instance"
|
|
24
|
+
|
|
25
|
+
def position(self, t: float) -> Tuple[np.ndarray, np.ndarray]:
|
|
26
|
+
return (self.x(t), self.y(t))
|
|
27
|
+
|
|
28
|
+
@cached_property
|
|
29
|
+
def has_lost(self) -> bool:
|
|
30
|
+
return any(event_type == "Lost" for _, event_type in self.events)
|
|
31
|
+
|
|
32
|
+
def is_lost(self, time: float) -> bool:
|
|
33
|
+
if not self.has_lost:
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
self.events[-1][0] <= time
|
|
38
|
+
) # TODO: maybe we need approx_eq to be separate package?
|
|
39
|
+
|
|
40
|
+
def to_json(self):
|
|
41
|
+
return {
|
|
42
|
+
"id": self.id,
|
|
43
|
+
"x": self.x.to_json(),
|
|
44
|
+
"y": self.y.to_json(),
|
|
45
|
+
"events": self.events,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def from_json(cls, json_dict):
|
|
50
|
+
return AtomTrajectory(
|
|
51
|
+
json_dict["id"],
|
|
52
|
+
PPoly.from_json(json_dict["x"]),
|
|
53
|
+
PPoly.from_json(json_dict["y"]),
|
|
54
|
+
list(map(tuple, json_dict["events"])),
|
|
55
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import scipy.interpolate as interp
|
|
7
|
+
|
|
8
|
+
from .utils import array_to_json, json_to_array
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclasses.dataclass
|
|
12
|
+
class PPoly:
|
|
13
|
+
c: np.ndarray
|
|
14
|
+
x: np.ndarray
|
|
15
|
+
extrapolate: bool = dataclasses.field(default=False)
|
|
16
|
+
|
|
17
|
+
def __post_init__(self):
|
|
18
|
+
super().__setattr__("x", np.asarray(self.x))
|
|
19
|
+
super().__setattr__("c", np.asarray(self.c))
|
|
20
|
+
|
|
21
|
+
def __eq__(self, other: Any) -> bool:
|
|
22
|
+
if not isinstance(other, PPoly):
|
|
23
|
+
return False
|
|
24
|
+
return (
|
|
25
|
+
np.array_equal(self.x, other.x)
|
|
26
|
+
and np.array_equal(self.c, other.c)
|
|
27
|
+
and self.extrapolate == other.extrapolate
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
@cached_property
|
|
31
|
+
def get_ppoly(self) -> interp.PPoly:
|
|
32
|
+
return interp.PPoly(self.c, self.x, self.extrapolate)
|
|
33
|
+
|
|
34
|
+
def __call__(self, x: float) -> float:
|
|
35
|
+
return self.get_ppoly(x)
|
|
36
|
+
|
|
37
|
+
def to_json(self) -> Dict[str, Any]:
|
|
38
|
+
return {
|
|
39
|
+
"c": array_to_json(self.c),
|
|
40
|
+
"x": array_to_json(self.x),
|
|
41
|
+
"extrapolate": self.extrapolate,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def from_json(cls, json_dict: Dict[str, Any]) -> "PPoly":
|
|
46
|
+
return cls(
|
|
47
|
+
c=json_to_array(json_dict["c"]),
|
|
48
|
+
x=json_to_array(json_dict["x"]),
|
|
49
|
+
extrapolate=json_dict["extrapolate"],
|
|
50
|
+
)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import Any, Dict, List, Tuple
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from .aod import AODMoveEvent
|
|
8
|
+
from ..base import FieldOfView
|
|
9
|
+
from .atoms import AtomTrajectory
|
|
10
|
+
from ..gate_event import GateEvent
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclasses.dataclass
|
|
14
|
+
class QPUStateABC(ABC):
|
|
15
|
+
|
|
16
|
+
block_durations: List[float] = dataclasses.field(default_factory=list)
|
|
17
|
+
gate_events: List[Tuple[float, GateEvent]] = dataclasses.field(default_factory=list)
|
|
18
|
+
qpu_fov: FieldOfView = dataclasses.field(default_factory=FieldOfView)
|
|
19
|
+
|
|
20
|
+
def __post_init__(self):
|
|
21
|
+
assert all(
|
|
22
|
+
isinstance(gate, GateEvent) for _, gate in self.gate_events
|
|
23
|
+
), "All gate events must be GateEvent instances"
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def get_slm_sites(self) -> np.ndarray: ...
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def sample_aod_traps(self, time: float) -> List[Tuple[float, float]]: ...
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def get_atoms_lost_info(self, time: float) -> List[str]: ...
|
|
33
|
+
|
|
34
|
+
@abstractmethod
|
|
35
|
+
def get_atoms_position(
|
|
36
|
+
self, time: float, include_lost: bool = False
|
|
37
|
+
) -> List[Tuple[int, Tuple[float, float]]]: ...
|
|
38
|
+
|
|
39
|
+
def get_gate_events_timing(self) -> List[Tuple[float, float]]:
|
|
40
|
+
# return [t_start, duration]
|
|
41
|
+
return [(t, gate.duration) for t, gate in self.gate_events]
|
|
42
|
+
|
|
43
|
+
def get_gate_events(self, time: float) -> List[Tuple[float, GateEvent]]:
|
|
44
|
+
out = []
|
|
45
|
+
if self.gate_events:
|
|
46
|
+
for t, gate in self.gate_events:
|
|
47
|
+
if t > time:
|
|
48
|
+
break
|
|
49
|
+
if t <= time < t + gate.duration:
|
|
50
|
+
out.append((t, gate))
|
|
51
|
+
return out
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclasses.dataclass
|
|
55
|
+
class AnimateQPUState(QPUStateABC):
|
|
56
|
+
atoms: List[AtomTrajectory] = dataclasses.field(default_factory=list)
|
|
57
|
+
slm_zone: List[Tuple[float, float]] = dataclasses.field(default_factory=list)
|
|
58
|
+
aod_moves: List[AODMoveEvent] = dataclasses.field(default_factory=list)
|
|
59
|
+
|
|
60
|
+
def __post_init__(self):
|
|
61
|
+
assert all(
|
|
62
|
+
isinstance(atom, AtomTrajectory) for atom in self.atoms
|
|
63
|
+
), "All atoms must be AtomTrajectory instances"
|
|
64
|
+
assert all(
|
|
65
|
+
isinstance(aod, AODMoveEvent) for aod in self.aod_moves
|
|
66
|
+
), "All AOD moves must be AODMoveEvent instances"
|
|
67
|
+
assert all(
|
|
68
|
+
isinstance(site, tuple) and len(site) == 2 for site in self.slm_zone
|
|
69
|
+
), "All SLM sites must be tuples of length 2"
|
|
70
|
+
|
|
71
|
+
def get_slm_sites(self):
|
|
72
|
+
return np.array(self.slm_zone)
|
|
73
|
+
|
|
74
|
+
def sample_aod_traps(self, time: float) -> List[Tuple[float, float]]:
|
|
75
|
+
out = []
|
|
76
|
+
for aod_trap in self.aod_moves:
|
|
77
|
+
if aod_trap.time <= time < aod_trap.time + aod_trap.duration:
|
|
78
|
+
out.append(aod_trap.sample(time))
|
|
79
|
+
|
|
80
|
+
return out
|
|
81
|
+
|
|
82
|
+
def get_atoms_lost_info(self, time: float) -> List[str]:
|
|
83
|
+
return [
|
|
84
|
+
f"Lost: {atom.id} @{atom.events[-1][0]:.3f} (us)" + "\n"
|
|
85
|
+
for atom in self.atoms
|
|
86
|
+
if atom.is_lost(time)
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
def get_atoms_position(
|
|
90
|
+
self, time: float, include_lost: bool = False
|
|
91
|
+
) -> List[Tuple[int, Tuple[float, float]]]:
|
|
92
|
+
return [
|
|
93
|
+
(atom.id, atom.position(time))
|
|
94
|
+
for atom in self.atoms
|
|
95
|
+
if not atom.is_lost(time) or include_lost
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
def to_json(self) -> Dict[str, Any]:
|
|
99
|
+
return {
|
|
100
|
+
"block_durations": self.block_durations,
|
|
101
|
+
"gate_events": [(t, gate.to_json()) for t, gate in self.gate_events],
|
|
102
|
+
"qpu_fov": self.qpu_fov.to_json(),
|
|
103
|
+
"atoms": [atom.to_json() for atom in self.atoms],
|
|
104
|
+
"slm_zone": self.slm_zone,
|
|
105
|
+
"aod_moves": [aod.to_json() for aod in self.aod_moves],
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@classmethod
|
|
109
|
+
def from_json(cls, json_dict: Dict[str, Any]) -> "AnimateQPUState":
|
|
110
|
+
return cls(
|
|
111
|
+
block_durations=json_dict["block_durations"],
|
|
112
|
+
gate_events=[
|
|
113
|
+
(t, GateEvent.from_json(gate)) for t, gate in json_dict["gate_events"]
|
|
114
|
+
],
|
|
115
|
+
qpu_fov=FieldOfView.from_json(json_dict["qpu_fov"]),
|
|
116
|
+
atoms=list(map(AtomTrajectory.from_json, json_dict["atoms"])),
|
|
117
|
+
slm_zone=list(map(tuple, json_dict["slm_zone"])),
|
|
118
|
+
aod_moves=list(map(AODMoveEvent.from_json, json_dict["aod_moves"])),
|
|
119
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import zlib
|
|
2
|
+
import base64
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def bytes_to_str(byte_array: bytes) -> str:
|
|
9
|
+
compressed_bytes = zlib.compress(byte_array)
|
|
10
|
+
return base64.b64encode(compressed_bytes).decode("ascii")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def str_to_bytes(string: str) -> bytes:
|
|
14
|
+
compressed_bytes = base64.b64decode(string)
|
|
15
|
+
return zlib.decompress(compressed_bytes)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def validate_dtype(dtype) -> None:
|
|
19
|
+
if np.dtype(dtype) == np.dtype(object):
|
|
20
|
+
raise ValueError("Object arrays are not supported")
|
|
21
|
+
elif dtype.subdtype is not None:
|
|
22
|
+
validate_dtype(dtype.subdtype[0])
|
|
23
|
+
elif dtype.fields is not None:
|
|
24
|
+
for _, (field_dtype, _) in dtype.fields.items():
|
|
25
|
+
validate_dtype(field_dtype)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def array_to_json(arr: np.ndarray) -> Dict[str, Any]:
|
|
29
|
+
validate_dtype(arr.dtype)
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
"dtype": (arr.dtype.descr if arr.dtype.fields is not None else arr.dtype.str),
|
|
33
|
+
"shape": arr.shape,
|
|
34
|
+
"buffer": bytes_to_str(arr.tobytes()),
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def json_to_array(json_dict: dict) -> np.ndarray:
|
|
39
|
+
_data = json_dict["buffer"]
|
|
40
|
+
_dtype = json_dict["dtype"]
|
|
41
|
+
_shape = json_dict["shape"]
|
|
42
|
+
|
|
43
|
+
return np.frombuffer(str_to_bytes(_data), dtype=_dtype).reshape(_shape)
|