QuLab 2.0.1__cp310-cp310-macosx_11_0_x86_64.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 (82) hide show
  1. QuLab-2.0.1.dist-info/LICENSE +21 -0
  2. QuLab-2.0.1.dist-info/METADATA +95 -0
  3. QuLab-2.0.1.dist-info/RECORD +82 -0
  4. QuLab-2.0.1.dist-info/WHEEL +5 -0
  5. QuLab-2.0.1.dist-info/entry_points.txt +2 -0
  6. QuLab-2.0.1.dist-info/top_level.txt +1 -0
  7. qulab/__init__.py +1 -0
  8. qulab/__main__.py +24 -0
  9. qulab/fun.cpython-310-darwin.so +0 -0
  10. qulab/monitor/__init__.py +1 -0
  11. qulab/monitor/__main__.py +8 -0
  12. qulab/monitor/config.py +41 -0
  13. qulab/monitor/dataset.py +77 -0
  14. qulab/monitor/event_queue.py +54 -0
  15. qulab/monitor/mainwindow.py +234 -0
  16. qulab/monitor/monitor.py +93 -0
  17. qulab/monitor/ploter.py +123 -0
  18. qulab/monitor/qt_compat.py +16 -0
  19. qulab/monitor/toolbar.py +265 -0
  20. qulab/scan/__init__.py +4 -0
  21. qulab/scan/base.py +548 -0
  22. qulab/scan/dataset.py +0 -0
  23. qulab/scan/expression.py +472 -0
  24. qulab/scan/optimize.py +0 -0
  25. qulab/scan/scanner.py +270 -0
  26. qulab/scan/transforms.py +16 -0
  27. qulab/scan/utils.py +37 -0
  28. qulab/storage/__init__.py +0 -0
  29. qulab/storage/__main__.py +51 -0
  30. qulab/storage/backend/__init__.py +0 -0
  31. qulab/storage/backend/redis.py +204 -0
  32. qulab/storage/base_dataset.py +352 -0
  33. qulab/storage/chunk.py +60 -0
  34. qulab/storage/dataset.py +127 -0
  35. qulab/storage/file.py +273 -0
  36. qulab/storage/models/__init__.py +22 -0
  37. qulab/storage/models/base.py +4 -0
  38. qulab/storage/models/config.py +28 -0
  39. qulab/storage/models/file.py +89 -0
  40. qulab/storage/models/ipy.py +58 -0
  41. qulab/storage/models/models.py +88 -0
  42. qulab/storage/models/record.py +161 -0
  43. qulab/storage/models/report.py +22 -0
  44. qulab/storage/models/tag.py +93 -0
  45. qulab/storage/storage.py +95 -0
  46. qulab/sys/__init__.py +0 -0
  47. qulab/sys/chat.py +688 -0
  48. qulab/sys/device/__init__.py +3 -0
  49. qulab/sys/device/basedevice.py +221 -0
  50. qulab/sys/device/loader.py +86 -0
  51. qulab/sys/device/utils.py +46 -0
  52. qulab/sys/drivers/FakeInstrument.py +52 -0
  53. qulab/sys/drivers/__init__.py +0 -0
  54. qulab/sys/ipy_events.py +125 -0
  55. qulab/sys/net/__init__.py +0 -0
  56. qulab/sys/net/bencoder.py +205 -0
  57. qulab/sys/net/cli.py +169 -0
  58. qulab/sys/net/dhcp.py +543 -0
  59. qulab/sys/net/dhcpd.py +176 -0
  60. qulab/sys/net/kad.py +1142 -0
  61. qulab/sys/net/kcp.py +192 -0
  62. qulab/sys/net/nginx.py +192 -0
  63. qulab/sys/progress.py +190 -0
  64. qulab/sys/rpc/__init__.py +0 -0
  65. qulab/sys/rpc/client.py +0 -0
  66. qulab/sys/rpc/exceptions.py +96 -0
  67. qulab/sys/rpc/msgpack.py +1052 -0
  68. qulab/sys/rpc/msgpack.pyi +41 -0
  69. qulab/sys/rpc/rpc.py +412 -0
  70. qulab/sys/rpc/serialize.py +139 -0
  71. qulab/sys/rpc/server.py +29 -0
  72. qulab/sys/rpc/socket.py +29 -0
  73. qulab/sys/rpc/utils.py +25 -0
  74. qulab/sys/rpc/worker.py +0 -0
  75. qulab/version.py +1 -0
  76. qulab/visualization/__init__.py +188 -0
  77. qulab/visualization/__main__.py +71 -0
  78. qulab/visualization/_autoplot.py +457 -0
  79. qulab/visualization/plot_layout.py +408 -0
  80. qulab/visualization/plot_seq.py +90 -0
  81. qulab/visualization/qdat.py +152 -0
  82. qulab/visualization/widgets.py +86 -0
@@ -0,0 +1,408 @@
1
+ import functools
2
+ import operator
3
+
4
+ import matplotlib.pyplot as plt
5
+ import numpy as np
6
+ from matplotlib import cm
7
+ from matplotlib.colors import Normalize
8
+
9
+ layout_example = {
10
+ 'qubits': {
11
+ 'Q0': {
12
+ 'pos': (0, 1)
13
+ },
14
+ 'Q1': {
15
+ 'pos': (1, 0)
16
+ },
17
+ 'Q2': {
18
+ 'pos': (0, -1)
19
+ },
20
+ 'Q3': {
21
+ 'pos': (-1, 0)
22
+ }
23
+ },
24
+ 'couplers': {
25
+ 'C0': {
26
+ 'qubits': ['Q0', 'Q1'],
27
+ },
28
+ 'C1': {
29
+ 'qubits': ['Q1', 'Q2'],
30
+ },
31
+ 'C2': {
32
+ 'qubits': ['Q2', 'Q3'],
33
+ },
34
+ 'C3': {
35
+ 'qubits': ['Q0', 'Q3'],
36
+ }
37
+ }
38
+ }
39
+
40
+
41
+ def complete_layout(layout):
42
+ for c in layout['couplers']:
43
+ qubits = layout['couplers'][c]['qubits']
44
+ for q in qubits:
45
+ if q not in layout['qubits']:
46
+ raise ValueError('qubit {} not found'.format(q))
47
+ if 'couplers' not in layout['qubits'][q]:
48
+ layout['qubits'][q]['couplers'] = []
49
+ if c not in layout['qubits'][q]['couplers']:
50
+ layout['qubits'][q]['couplers'].append(c)
51
+ return layout
52
+
53
+
54
+ def get_shared_coupler(layout, q1, q2):
55
+ for c in layout['qubits'][q1]['couplers']:
56
+ if q2 in layout['couplers'][c]['qubits']:
57
+ return c
58
+ return None
59
+
60
+
61
+ def get_neighbours(layout,
62
+ qubit_or_coupler,
63
+ distance=1,
64
+ type='qubit',
65
+ inrange=False):
66
+
67
+ def _qubits(couplers):
68
+ ret = set()
69
+ for c in couplers:
70
+ ret = ret | set(layout['couplers'][c]['qubits'])
71
+ return ret
72
+
73
+ def _couplers(qubits):
74
+ ret = set()
75
+ for q in qubits:
76
+ ret = ret | set(layout['qubits'][q]['couplers'])
77
+ return ret
78
+
79
+ couplers = []
80
+ neighbors = []
81
+
82
+ if qubit_or_coupler in layout['qubits']:
83
+ couplers.append(set(layout['qubits'][qubit_or_coupler]['couplers']))
84
+ neighbors.append(_qubits(couplers[0]) - {qubit_or_coupler})
85
+ elif qubit_or_coupler in layout['couplers']:
86
+ if type == 'coupler':
87
+ distance += 1
88
+ neighbors.append(set(layout['couplers'][qubit_or_coupler]['qubits']))
89
+ couplers.append({qubit_or_coupler})
90
+ else:
91
+ raise ValueError(f'qubit or coupler {qubit_or_coupler!r} not found')
92
+ distance -= 1
93
+
94
+ while distance > 0:
95
+ couplers.append(_couplers(neighbors[-1]) - couplers[-1])
96
+ neighbors.append(_qubits(couplers[-1]) - neighbors[-1])
97
+ distance -= 1
98
+
99
+ if type == 'qubit':
100
+ if inrange:
101
+ return list(functools.reduce(operator.or_, neighbors, set()))
102
+ else:
103
+ return list(neighbors[-1])
104
+ elif type == 'coupler':
105
+ if inrange:
106
+ if qubit_or_coupler in couplers[0]:
107
+ couplers = couplers[1:]
108
+ return list(functools.reduce(operator.or_, couplers, set()))
109
+ else:
110
+ return list(couplers[-1])
111
+ else:
112
+ raise ValueError("type must be 'qubit' or 'coupler'")
113
+
114
+
115
+ def plot_range(ax,
116
+ path,
117
+ text='',
118
+ color=None,
119
+ text_color='k',
120
+ bounder_color='k',
121
+ lw=0.5,
122
+ fontsize=9):
123
+ x, y = path
124
+ center = x.mean(), y.mean()
125
+
126
+ if color:
127
+ ax.fill(x, y, color=color, lw=0)
128
+
129
+ if lw is not None and lw > 0:
130
+ ax.plot(np.hstack([x, [x[0]]]),
131
+ np.hstack([y, [y[0]]]),
132
+ color=bounder_color,
133
+ lw=lw)
134
+
135
+ if text:
136
+ ax.text(center[0],
137
+ center[1],
138
+ text,
139
+ ha='center',
140
+ va='center',
141
+ color=text_color,
142
+ fontsize=fontsize)
143
+
144
+
145
+ def circle_path(pos, r, n=40):
146
+ x, y = pos
147
+ t = 2 * np.pi * np.linspace(0, 1, n, endpoint=False)
148
+ xx = r * np.cos(t) + x
149
+ yy = r * np.sin(t) + y
150
+ return xx, yy
151
+
152
+
153
+ def circle_link_path(pos1, pos2, r1, r2, width, n=20):
154
+ width = min(2 * max(r1, r2), width)
155
+
156
+ x1, y1 = pos1
157
+ x2, y2 = pos2
158
+
159
+ phi = np.arctan2(y2 - y1, x2 - x1)
160
+
161
+ theta1 = np.arcsin(width / 2 / r1)
162
+ theta2 = np.arcsin(width / 2 / r2)
163
+
164
+ t = np.linspace(-theta1, theta1, n) + phi
165
+ xx1 = r1 * np.cos(t) + x1
166
+ yy1 = r1 * np.sin(t) + y1
167
+
168
+ t = np.linspace(-theta2, theta2, n) + phi + np.pi
169
+ xx2 = r2 * np.cos(t) + x2
170
+ yy2 = r2 * np.sin(t) + y2
171
+
172
+ return np.hstack([xx2[-1], xx1,
173
+ xx2[:-1]]), np.hstack([yy2[-1], yy1, yy2[:-1]])
174
+
175
+
176
+ def circle_half_directed_link_path(pos1, pos2, r1, r2, width, n=20):
177
+ width = min(max(r1, r2), width)
178
+
179
+ x1, y1 = pos1
180
+ x2, y2 = pos2
181
+
182
+ phi = np.arctan2(y2 - y1, x2 - x1)
183
+
184
+ theta1 = np.arcsin(width / r1)
185
+ theta2 = np.arcsin(width / r2)
186
+
187
+ t = np.linspace(0.2 * theta1, theta1, n) + phi
188
+ xx1 = r1 * np.cos(t) + x1
189
+ yy1 = r1 * np.sin(t) + y1
190
+
191
+ t = np.linspace(-theta2, -0.2 * theta2, n) + phi + np.pi
192
+ xx2 = r2 * np.cos(t) + x2
193
+ yy2 = r2 * np.sin(t) + y2
194
+
195
+ v = (xx2[0] - xx1[-1]) + 1j * (yy2[0] - yy1[-1])
196
+ c = (xx2[0] + xx1[-1]) / 2 + 1j * (yy2[0] + yy1[-1]) / 2
197
+
198
+ a = np.array([1 / 6, 1 / 12]) + 1j * np.array([0, 0.4 * width / np.abs(v)])
199
+ a = a * v + c
200
+
201
+ return np.hstack([xx2[-1], xx1, a.real,
202
+ xx2[:-1]]), np.hstack([yy2[-1], yy1, a.imag, yy2[:-1]])
203
+
204
+
205
+ def draw(layout, ax=None, qubit_cbar=True, coupler_cbar=True, origin='upper'):
206
+ if ax is None:
207
+ ax = plt.gca()
208
+
209
+ for qubit in layout['qubits'].values():
210
+ pos = qubit['pos']
211
+ if origin == 'upper':
212
+ pos = pos[0], -pos[1]
213
+ path = circle_path(pos, qubit.get('radius', 0.5))
214
+ plot_range(ax,
215
+ path,
216
+ qubit.get('text', ''),
217
+ qubit.get('color', None),
218
+ lw=qubit.get('lw', 0.5),
219
+ fontsize=qubit.get('fontsize', 9),
220
+ text_color=qubit.get('text_color', 'k'),
221
+ bounder_color=qubit.get('bounder_color', 'k'))
222
+
223
+ for coupler in layout['couplers'].values():
224
+ q1, q2 = coupler['qubits']
225
+ pos1 = layout['qubits'][q1]['pos']
226
+ pos2 = layout['qubits'][q2]['pos']
227
+ if origin == 'upper':
228
+ pos1 = pos1[0], -pos1[1]
229
+ pos2 = pos2[0], -pos2[1]
230
+ r1 = layout['qubits'][q1].get('radius', 0.5)
231
+ r2 = layout['qubits'][q2].get('radius', 0.5)
232
+ width = coupler.get('width', 0.5)
233
+ lw = coupler.get('lw', 0.5)
234
+
235
+ path = circle_link_path(pos1, pos2, r1, r2, width)
236
+ plot_range(ax,
237
+ path,
238
+ coupler.get('text', ''),
239
+ color=coupler.get('color', None),
240
+ lw=0,
241
+ fontsize=coupler.get('fontsize', 9),
242
+ text_color=coupler.get('text_color', 'k'))
243
+ if lw > 0:
244
+ x, y = circle_link_path(pos1, pos2, r1, r2, width, n=2)
245
+ ax.plot(x[:2],
246
+ y[:2],
247
+ lw=lw,
248
+ color=coupler.get('bounder_color', 'k'))
249
+ ax.plot(x[2:],
250
+ y[2:],
251
+ lw=lw,
252
+ color=coupler.get('bounder_color', 'k'))
253
+
254
+ ax.axis('equal')
255
+ ax.set_axis_off()
256
+
257
+ if qubit_cbar and layout['__colorbar__']['qubit']['norm'] is not None:
258
+ cbar = plt.colorbar(cm.ScalarMappable(
259
+ norm=layout['__colorbar__']['qubit']['norm'],
260
+ cmap=layout['__colorbar__']['qubit']['cmap']),
261
+ ax=ax,
262
+ location='bottom',
263
+ orientation='horizontal',
264
+ pad=0.01,
265
+ shrink=0.5)
266
+ cbar.set_label(layout['__colorbar__']['qubit']['label'])
267
+ if coupler_cbar and layout['__colorbar__']['coupler']['norm'] is not None:
268
+ cbar = plt.colorbar(cm.ScalarMappable(
269
+ norm=layout['__colorbar__']['coupler']['norm'],
270
+ cmap=layout['__colorbar__']['coupler']['cmap']),
271
+ ax=ax,
272
+ location='bottom',
273
+ orientation='horizontal',
274
+ pad=0.01,
275
+ shrink=0.5)
276
+ cbar.set_label(layout['__colorbar__']['coupler']['label'])
277
+
278
+
279
+ def get_norm(params, elms, vmin=None, vmax=None):
280
+ data = []
281
+ for elm in elms:
282
+ if elm in params:
283
+ if isinstance(params[elm], (int, float)):
284
+ data.append(params[elm])
285
+ elif 'value' in params[elm] and params[elm]['value'] is not None:
286
+ data.append(params[elm]['value'])
287
+ if data:
288
+ if vmin is None:
289
+ vmin = min(data)
290
+ if vmax is None:
291
+ vmax = max(data)
292
+ return Normalize(vmin=vmin, vmax=vmax)
293
+ else:
294
+ return None
295
+
296
+
297
+ def fill_layout(layout,
298
+ params,
299
+ qubit_size=0.5,
300
+ coupler_size=0.5,
301
+ qubit_fontsize=9,
302
+ coupler_fontsize=9,
303
+ qubit_color=None,
304
+ coupler_color=None,
305
+ qubit_cmap='hot',
306
+ qubit_vmax=None,
307
+ qubit_vmin=None,
308
+ qubit_norm=None,
309
+ coupler_cmap='binary',
310
+ coupler_vmax=None,
311
+ coupler_vmin=None,
312
+ coupler_norm=None,
313
+ bounder_color='k',
314
+ lw=0.5):
315
+
316
+ qubit_cmap = plt.get_cmap(qubit_cmap)
317
+ coupler_cmap = plt.get_cmap(coupler_cmap)
318
+
319
+ if qubit_norm is None:
320
+ qubit_norm = get_norm(params,
321
+ layout['qubits'].keys(),
322
+ vmin=qubit_vmin,
323
+ vmax=qubit_vmax)
324
+ if coupler_norm is None:
325
+ coupler_norm = get_norm(params,
326
+ layout['couplers'].keys(),
327
+ vmin=coupler_vmin,
328
+ vmax=coupler_vmax)
329
+ layout['__colorbar__'] = {
330
+ 'coupler': {
331
+ 'cmap': coupler_cmap,
332
+ 'norm': coupler_norm,
333
+ 'label': ''
334
+ },
335
+ 'qubit': {
336
+ 'cmap': qubit_cmap,
337
+ 'norm': qubit_norm,
338
+ 'label': ''
339
+ }
340
+ }
341
+
342
+ for qubit in layout['qubits']:
343
+ layout['qubits'][qubit]['radius'] = qubit_size
344
+ layout['qubits'][qubit]['fontsize'] = qubit_fontsize
345
+ if qubit in params:
346
+ layout['qubits'][qubit]['lw'] = 0
347
+ if not isinstance(params[qubit], dict):
348
+ params[qubit] = {'value': params[qubit]}
349
+ if 'color' in params[qubit]:
350
+ layout['qubits'][qubit]['color'] = params[qubit]['color']
351
+ elif 'value' in params[qubit] and params[qubit][
352
+ 'value'] is not None:
353
+ layout['qubits'][qubit]['color'] = qubit_cmap(
354
+ qubit_norm(params[qubit]['value']))
355
+ else:
356
+ layout['qubits'][qubit]['color'] = qubit_color
357
+ if qubit_color is None:
358
+ layout['qubits'][qubit]['lw'] = lw
359
+ layout['qubits'][qubit]['radius'] = params[qubit].get(
360
+ 'radius', qubit_size)
361
+ layout['qubits'][qubit]['fontsize'] = params[qubit].get(
362
+ 'fontsize', qubit_fontsize)
363
+ layout['qubits'][qubit]['text'] = params[qubit].get('text', '')
364
+ layout['qubits'][qubit]['text_color'] = params[qubit].get(
365
+ 'text_color', 'k')
366
+ else:
367
+ layout['qubits'][qubit]['color'] = qubit_color
368
+ if qubit_color is None:
369
+ layout['qubits'][qubit]['lw'] = lw
370
+ else:
371
+ layout['qubits'][qubit]['lw'] = 0
372
+ layout['qubits'][qubit]['bounder_color'] = bounder_color
373
+
374
+ for coupler in layout['couplers']:
375
+ layout['couplers'][coupler]['width'] = coupler_size
376
+ layout['couplers'][coupler]['fontsize'] = coupler_fontsize
377
+ layout['couplers'][coupler]['bounder_color'] = bounder_color
378
+ if coupler in params:
379
+ layout['couplers'][coupler]['lw'] = 0
380
+ if not isinstance(params[coupler], dict):
381
+ params[coupler] = {'value': params[coupler]}
382
+ if 'color' in params[coupler]:
383
+ layout['couplers'][coupler]['color'] = params[coupler]['color']
384
+ elif 'value' in params[coupler] and params[coupler][
385
+ 'value'] is not None:
386
+ layout['couplers'][coupler]['color'] = coupler_cmap(
387
+ coupler_norm(params[coupler]['value']))
388
+ else:
389
+ layout['couplers'][coupler]['color'] = coupler_color
390
+ if coupler_color is None:
391
+ layout['couplers'][qubit]['lw'] = lw
392
+ layout['couplers'][coupler]['width'] = params[coupler].get(
393
+ 'width', coupler_size)
394
+ layout['couplers'][coupler]['fontsize'] = params[coupler].get(
395
+ 'fontsize', coupler_fontsize)
396
+ layout['couplers'][coupler]['text'] = params[coupler].get(
397
+ 'text', '')
398
+ layout['couplers'][coupler]['text_color'] = params[coupler].get(
399
+ 'text_color', 'k')
400
+ else:
401
+ layout['couplers'][coupler]['color'] = coupler_color
402
+ if coupler_color is None:
403
+ layout['couplers'][coupler]['lw'] = lw
404
+ else:
405
+ layout['couplers'][coupler]['lw'] = 0
406
+ layout['couplers'][coupler]['bounder_color'] = bounder_color
407
+
408
+ return layout
@@ -0,0 +1,90 @@
1
+ import itertools
2
+
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+
6
+
7
+ def cycle(x0, y0, r, a=0, b=2 * np.pi):
8
+ t = np.linspace(a, b, 101)
9
+ x = r * np.cos(t) + x0
10
+ y = r * np.sin(t) + y0
11
+ return x, y
12
+
13
+
14
+ def plotSquare(xc, yc, xs, ys, radius=0, color='C0', ax=None):
15
+ ax = plt.gca() if ax is None else ax
16
+
17
+ l, r, b, u = xc - xs / 2, xc + xs / 2, yc - ys / 2, yc + ys / 2
18
+ x = np.linspace(l + radius, r - radius, 2)
19
+ y = np.linspace(b + radius, u - radius, 2)
20
+
21
+ ax.plot(x, [u, u], color=color)
22
+ ax.plot(x, [b, b], color=color)
23
+ ax.plot([l, l], y, color=color)
24
+ ax.plot([r, r], y, color=color)
25
+
26
+ xx, yy = cycle(x[-1], y[-1], radius, 0, np.pi / 2)
27
+ ax.plot(xx, yy, color=color)
28
+
29
+ xx, yy = cycle(x[0], y[-1], radius, np.pi / 2, np.pi)
30
+ ax.plot(xx, yy, color=color)
31
+
32
+ xx, yy = cycle(x[0], y[0], radius, np.pi, np.pi * 3 / 2)
33
+ ax.plot(xx, yy, color=color)
34
+
35
+ xx, yy = cycle(x[-1], y[0], radius, np.pi * 3 / 2, np.pi * 2)
36
+ ax.plot(xx, yy, color=color)
37
+
38
+
39
+ def plotMesure(x0=0, y0=0, size=1, color='C0', ax=None):
40
+
41
+ ax = plt.gca() if ax is None else ax
42
+
43
+ x, y = cycle(x0, y0 - 0.37 * size, 0.57 * size, np.pi / 4, np.pi * 3 / 4)
44
+ ax.plot(x, y, color=color)
45
+
46
+ plotSquare(x0, y0, size, 0.8 * size, radius=0.2 * size, color=color, ax=ax)
47
+
48
+ angle = np.pi / 6
49
+
50
+ ax.arrow(x0,
51
+ y0 - 0.25 * size,
52
+ 0.55 * size * np.sin(angle),
53
+ 0.55 * size * np.cos(angle),
54
+ head_width=0.07 * size,
55
+ head_length=0.1 * size,
56
+ color=color)
57
+
58
+
59
+ def plot_seq(ax, waves, measure=[1], gap=4, maxTime=20, xlim=None):
60
+ t = np.linspace(0, maxTime, 1001)
61
+ tt = np.linspace(0, maxTime - gap, 1001)
62
+ styles = ['-', '--', ':', '-.']
63
+ colors = ['C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9']
64
+ for i, (wavgroup, color) in enumerate(zip(waves, itertools.cycle(colors))):
65
+ if i in measure:
66
+ time = tt
67
+ else:
68
+ time = t
69
+ if not isinstance(wavgroup, tuple):
70
+ wavgroup = (wavgroup, )
71
+
72
+ for wav, style in zip(wavgroup, itertools.cycle(styles)):
73
+ ax.plot(time, wav(time) - gap * i, style, color=color)
74
+ if i in measure:
75
+ plotMesure(maxTime - gap / 2,
76
+ -gap * i,
77
+ gap,
78
+ color='black',
79
+ ax=ax)
80
+
81
+ ax.axis('equal')
82
+
83
+ ax.spines['left'].set_visible(False)
84
+ ax.spines['right'].set_visible(False)
85
+ ax.spines['bottom'].set_visible(False)
86
+ ax.spines['top'].set_visible(False)
87
+ ax.set_xticks([])
88
+ ax.set_yticks([])
89
+ if xlim is not None:
90
+ ax.set_xlim(xlim)
@@ -0,0 +1,152 @@
1
+ """
2
+ This module provides the visualization of the qdat file.
3
+ """
4
+ import logging
5
+ import math
6
+ import re
7
+ from functools import reduce
8
+
9
+ import matplotlib.pyplot as plt
10
+ import numpy as np
11
+ from matplotlib import cm
12
+
13
+ log = logging.getLogger(__name__)
14
+
15
+
16
+ def draw(record_dict, fig=None, transpose=True, remove=True):
17
+ """Draw a 2D or 3D plot from a record dict.
18
+
19
+ Args:
20
+ record_dict (dict): The record dict.
21
+ fig (matplotlib.figure.Figure): The figure to draw on.
22
+ transpose (bool): Whether to transpose the data.
23
+ """
24
+ dim = record_dict['info']['dim']
25
+ zshape = record_dict['info']['zshape']
26
+ zsize = reduce(lambda a, b: a * b, zshape, 1)
27
+
28
+ axis_label = np.asarray(record_dict['info'].get('axis_label',
29
+ [])).flatten()
30
+ z_label = np.asarray(record_dict['info'].get('z_label', [])).flatten()
31
+ axis_unit = np.asarray(record_dict['info'].get('axis_unit', [])).flatten()
32
+ z_unit = np.asarray(record_dict['info'].get('z_unit', [])).flatten()
33
+
34
+ fig = plt.figure() if fig is None else fig
35
+ if zsize < 4:
36
+ plot_shape = (1, zsize)
37
+ else:
38
+ n = int(np.sqrt(zsize))
39
+ plot_shape = (math.ceil(zsize / n), n)
40
+ if dim < 3 and zsize < 100:
41
+ figsize = plot_shape[1] * 8, plot_shape[0] * 6
42
+ fig.set_size_inches(*figsize)
43
+ axes = fig.subplots(*plot_shape)
44
+ axes = np.array(axes).flatten()
45
+ else:
46
+ axes = []
47
+
48
+ cb_list = []
49
+ if dim == 1 and zsize < 101:
50
+ for i in range(zsize):
51
+ title = record_dict[
52
+ 'name'] + f' {i}' if zsize > 1 else record_dict['name']
53
+ try:
54
+ xlabel = axis_label[0]
55
+ ylabel = z_label[0] if len(z_label) == 1 else z_label[i]
56
+ except:
57
+ xlabel, ylabel = 'X', 'Y'
58
+ try:
59
+ x_unit = axis_unit[0]
60
+ y_unit = z_unit[0] if len(z_unit) == 1 else z_unit[i]
61
+ except:
62
+ x_unit, y_unit = 'a.u.', 'a.u.'
63
+ axes[i].set_title(title)
64
+ axes[i].set_xlabel(f'{xlabel} ({x_unit})')
65
+ axes[i].set_ylabel(f'{ylabel} ({y_unit})')
66
+ elif dim == 2 and zsize < 101:
67
+ for i in range(zsize):
68
+ smp = cm.ScalarMappable(norm=None, cmap=None)
69
+ cb = fig.colorbar(smp, ax=axes[i]) # 默认色谱的colorbar
70
+ cb_list.append(cb)
71
+ try:
72
+ title = record_dict['name'] + \
73
+ f': {z_label[i]}' if zsize > 1 else record_dict['name']
74
+ except:
75
+ title = record_dict[
76
+ 'name'] + f' {i}' if zsize > 1 else record_dict['name']
77
+ try:
78
+ xlabel, ylabel = axis_label[1], axis_label[0]
79
+ if transpose:
80
+ xlabel, ylabel = ylabel, xlabel
81
+ except:
82
+ xlabel, ylabel = 'X', 'Y'
83
+ try:
84
+ x_unit, y_unit = axis_unit[1], axis_unit[0]
85
+ if transpose:
86
+ x_unit, y_unit = y_unit, x_unit
87
+ except:
88
+ x_unit, y_unit = 'a.u.', 'a.u.'
89
+ axes[i].set_title(title)
90
+ axes[i].set_xlabel(f'{xlabel} ({x_unit})')
91
+ axes[i].set_ylabel(f'{ylabel} ({y_unit})')
92
+ else:
93
+ message1 = f'dim {dim} is too large (>2)! ' if dim > 2 else f'dim={dim}; '
94
+ message2 = f'zsize {zsize} is too large (>101)!' if zsize > 101 else f'zsize={zsize}'
95
+ log.warning('PASS: ' + message1 + message2)
96
+ #########################################################################################
97
+ try:
98
+ tags = [
99
+ tag.strip(r'\*')
100
+ for tag in record_dict['ParaSpace'].get('tags', [])
101
+ if re.match(r'\*', tag)
102
+ ]
103
+ tag_text = ','.join(tags)
104
+ if tag_text:
105
+ axes[0].text(-0.1,
106
+ 1.1,
107
+ 'TAG: ' + tag_text,
108
+ horizontalalignment='left',
109
+ verticalalignment='bottom',
110
+ transform=axes[0].transAxes) # fig.transFigure)#
111
+ except:
112
+ pass
113
+ #########################################################################################
114
+ fig.tight_layout()
115
+
116
+ dim = record_dict['info']['dim']
117
+ zshape = record_dict['info']['zshape']
118
+ datashape = record_dict['info']['datashape']
119
+ zsize = reduce(lambda a, b: a * b, zshape, 1)
120
+ datashape_r = (*datashape[:dim], zsize)
121
+
122
+ if dim == 1 and zsize < 101:
123
+ x, z = record_dict['data']
124
+ z = z.reshape(datashape_r)
125
+ z = np.abs(z) if np.any(np.iscomplex(z)) else z.real
126
+ for i in range(zsize):
127
+ _ = [a.remove() for a in axes[i].get_lines()] if remove else []
128
+ axes[i].plot(x, z[:, i], 'C0')
129
+ axes[i].plot(x, z[:, i], 'C0.')
130
+ elif dim == 2 and zsize < 101:
131
+ x, y, z = record_dict['data']
132
+ x_step, y_step = x[1] - x[0], y[1] - y[0]
133
+ if transpose:
134
+ x, y = y, x
135
+ x_step, y_step = y_step, x_step
136
+ z = z.reshape(datashape_r)
137
+ z = np.abs(z) if np.any(np.iscomplex(z)) else z.real
138
+ for i in range(zsize):
139
+ _z = z[:, :, i]
140
+ _ = [a.remove() for a in axes[i].get_images()] if remove else []
141
+ if transpose:
142
+ _z = _z.T
143
+ im = axes[i].imshow(_z,
144
+ extent=(y[0] - y_step / 2, y[-1] + y_step / 2,
145
+ x[0] - x_step / 2, x[-1] + x_step / 2),
146
+ origin='lower',
147
+ aspect='auto')
148
+ cb_list[i].update_normal(im) # 更新对应的colorbar
149
+ else:
150
+ pass
151
+
152
+ return fig, axes