QuLab 2.11.5__py3-none-any.whl → 2.11.7__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.
qulab/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2.11.5"
1
+ __version__ = "2.11.7"
@@ -6,6 +6,8 @@ import numpy as np
6
6
  from matplotlib import cm
7
7
  from matplotlib.colors import Normalize
8
8
 
9
+ from .rot3d import projection, rot_round
10
+
9
11
  layout_example = {
10
12
  'qubits': {
11
13
  'Q0': {
@@ -118,6 +120,7 @@ def plot_range(ax,
118
120
  color=None,
119
121
  text_color='k',
120
122
  bounder_color='k',
123
+ rotation=0,
121
124
  lw=0.5,
122
125
  fontsize=9):
123
126
  x, y = path
@@ -138,6 +141,7 @@ def plot_range(ax,
138
141
  text,
139
142
  ha='center',
140
143
  va='center',
144
+ rotation=rotation,
141
145
  color=text_color,
142
146
  fontsize=fontsize)
143
147
 
@@ -150,7 +154,7 @@ def circle_path(pos, r, n=40):
150
154
  return xx, yy
151
155
 
152
156
 
153
- def circle_link_path(pos1, pos2, r1, r2, width, n=20):
157
+ def circle_link_path(pos1, pos2, r1, r2, width, n=2):
154
158
  width = min(2 * max(r1, r2), width)
155
159
 
156
160
  x1, y1 = pos1
@@ -202,54 +206,185 @@ def circle_half_directed_link_path(pos1, pos2, r1, r2, width, n=20):
202
206
  xx2[:-1]]), np.hstack([yy2[-1], yy1, a.imag, yy2[:-1]])
203
207
 
204
208
 
205
- def draw(layout, ax=None, qubit_cbar=True, coupler_cbar=True, origin='upper'):
209
+ def project(pos, camera=(0, 0, 100), rotation=None):
210
+ if rotation is None:
211
+ rotation = {
212
+ 'axis': (0, 0, 1),
213
+ 'angle': 0,
214
+ 'center': (0, 0, 0),
215
+ }
216
+ x0, y0, z0 = camera
217
+ if len(pos) == 2:
218
+ x, y, z = [*pos, 0]
219
+ else:
220
+ x, y, z = pos
221
+ x, y, z = rot_round(x, y, z, rotation['axis'], rotation['angle'],
222
+ rotation['center'])
223
+ x, y, d = projection(x - x0, y - y0, z, d0=z0)
224
+ return x + x0, y + y0
225
+
226
+
227
+ def _draw_qubit(ax, qubit, pos):
228
+ path = circle_path(pos, qubit.get('radius', 0.5))
229
+ plot_range(ax,
230
+ path,
231
+ qubit.get('text', ''),
232
+ qubit.get('color', None),
233
+ lw=qubit.get('lw', 0.5),
234
+ fontsize=qubit.get('fontsize', 9),
235
+ rotation=qubit.get('text_rotation', 0),
236
+ text_color=qubit.get('text_color', 'k'),
237
+ bounder_color=qubit.get('bounder_color', 'k'))
238
+
239
+
240
+ def _draw_coupler(ax, coupler, layout, q1, q2, pos1, pos2):
241
+ r1 = layout['qubits'][q1].get('radius', 0.5)
242
+ r2 = layout['qubits'][q2].get('radius', 0.5)
243
+ width = coupler.get('width', 0.5)
244
+ lw = coupler.get('lw', 0.5)
245
+
246
+ text_rotation = 180 * np.arctan2(pos2[1] - pos1[1],
247
+ pos2[0] - pos1[0]) / np.pi
248
+
249
+ path = circle_link_path(pos1, pos2, r1, r2, width)
250
+ plot_range(ax,
251
+ path,
252
+ coupler.get('text', ''),
253
+ color=coupler.get('color', None),
254
+ lw=0,
255
+ fontsize=coupler.get('fontsize', 9),
256
+ rotation=coupler.get('text_rotation', text_rotation),
257
+ text_color=coupler.get('text_color', 'k'))
258
+ if lw > 0:
259
+ x, y = circle_link_path(pos1, pos2, r1, r2, width, n=2)
260
+ ax.plot(x[:2], y[:2], lw=lw, color=coupler.get('bounder_color', 'k'))
261
+ ax.plot(x[2:], y[2:], lw=lw, color=coupler.get('bounder_color', 'k'))
262
+
263
+
264
+ def get_range(layout):
265
+ coods = []
266
+ for q in layout['qubits']:
267
+ pos = layout['qubits'][q]['pos']
268
+ if len(pos) == 2:
269
+ x, y, z = [*pos, 0]
270
+ else:
271
+ x, y, z = pos
272
+ coods.append([x, y, z])
273
+ coods = np.array(coods)
274
+
275
+ center = coods.mean(axis=0)
276
+ radius = np.max(np.sqrt(np.sum((coods - center)**2, axis=1)))
277
+
278
+ return center, radius
279
+
280
+
281
+ def sorted_by_distance(layout, camera, rotation):
282
+ qubits = []
283
+ camera = np.array(camera)
284
+ for q in layout['qubits']:
285
+ pos = layout['qubits'][q]['pos']
286
+ if len(pos) == 2:
287
+ x, y, z = [*pos, 0]
288
+ else:
289
+ x, y, z = pos
290
+ x, y, z = rot_round(x, y, z, rotation['axis'], rotation['angle'],
291
+ rotation['center'])
292
+ d = np.sqrt(np.sum((np.array([x, y, z]) - camera)**2))
293
+ qubits.append((d, q))
294
+ qubits = sorted(qubits, key=lambda x: x[0])
295
+
296
+ plot_order = []
297
+ for _, q in reversed(qubits):
298
+ for c in layout['qubits'][q]['couplers']:
299
+ if ('C', c) not in plot_order:
300
+ plot_order.append(('C', c))
301
+ plot_order.append(('Q', q))
302
+ return plot_order
303
+
304
+
305
+ def draw(layout,
306
+ ax=None,
307
+ qubit_cbar=True,
308
+ coupler_cbar=True,
309
+ origin='upper',
310
+ camera=None,
311
+ rotation=None):
312
+ """
313
+ Draw a layout with qubits and couplers.
314
+
315
+ Parameters
316
+ ----------
317
+ layout: dict
318
+ A dictionary containing the layout of the qubits and couplers.
319
+ The dictionary should have the following structure:
320
+ {
321
+ 'qubits': {
322
+ 'Q0': {'pos': (x, y), 'radius': r, 'text': 'Q0'},
323
+ ...
324
+ },
325
+ 'couplers': {
326
+ 'C0': {'qubits': ['Q0', 'Q1'], 'width': w, 'text': 'C0'},
327
+ ...
328
+ }
329
+ }
330
+ ax: matplotlib.axes.Axes
331
+ The axes on which to draw the layout. If None, the current axes will be used.
332
+ qubit_cbar: bool
333
+ Whether to draw a colorbar for the qubits.
334
+ coupler_cbar: bool
335
+ Whether to draw a colorbar for the couplers.
336
+ origin: str
337
+ The origin of the layout. If 'upper', the y-coordinates will be inverted.
338
+ camera: tuple
339
+ The position of the camera in 3D space. Default is (0, 0, 1).
340
+ rotation: dict
341
+ The rotation of the layout. The dictionary should have the following structure:
342
+ {
343
+ 'axis': (x, y, z),
344
+ 'angle': angle,
345
+ 'center': (x, y, z)
346
+ }
347
+ If None, no rotation will be applied.
348
+ """
206
349
  if ax is None:
207
350
  ax = plt.gca()
208
351
 
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'))
352
+ center, radius = get_range(layout)
353
+
354
+ if rotation is None:
355
+ rotation = {
356
+ 'axis': (0, 0, 1),
357
+ 'angle': 0,
358
+ }
359
+ if 'center' not in rotation:
360
+ rotation['center'] = center
361
+ if camera is None:
362
+ camera = (center[0], center[1], center[2] + radius * 5)
363
+
364
+ plot_order = sorted_by_distance(layout, camera, rotation)
365
+
366
+ if origin == 'upper':
367
+ camera = [camera[0], -camera[1], camera[2]]
368
+ for kind, name in plot_order:
369
+ if kind == 'Q':
370
+ pos = layout['qubits'][name]['pos']
371
+ if origin == 'upper':
372
+ pos = [pos[0], -pos[1], *pos[2:]]
373
+ pos = project(pos, camera, rotation)
374
+ _draw_qubit(ax, layout['qubits'][name], pos)
375
+ elif kind == 'C':
376
+ coupler = layout['couplers'][name]
377
+ q1, q2 = coupler['qubits']
378
+ pos1 = layout['qubits'][q1]['pos']
379
+ pos2 = layout['qubits'][q2]['pos']
380
+ if origin == 'upper':
381
+ pos1 = [pos1[0], -pos1[1], *pos1[2:]]
382
+ pos2 = [pos2[0], -pos2[1], *pos2[2:]]
383
+ pos1 = project(pos1, camera, rotation)
384
+ pos2 = project(pos2, camera, rotation)
385
+ _draw_coupler(ax, coupler, layout, q1, q2, pos1, pos2)
386
+ else:
387
+ pass
253
388
 
254
389
  ax.axis('equal')
255
390
  ax.set_axis_off()
@@ -14,10 +14,36 @@ def _rot(theta, v):
14
14
 
15
15
 
16
16
  def rot_round(x, y, z, v=[0, 0, 1], theta=0, c=[0, 0, 0]):
17
+ """
18
+ Rotate a point (x, y, z) around a vector v by an angle theta
19
+
20
+ Parameters
21
+ ----------
22
+ x, y, z: coordinates of the point to be rotated
23
+ v: vector to rotate around
24
+ theta: angle to rotate by (in radians)
25
+ c: center of rotation
26
+
27
+ Returns
28
+ -------
29
+ ret: rotated coordinates
30
+ """
17
31
  ret = (np.array([x, y, z]).T - np.array(c)) @ _rot(theta, v) + np.array(c)
18
32
  return ret.T
19
33
 
20
34
 
21
- def projection(x, y, z, y0=1):
35
+ def projection(x, y, z, d0=1):
36
+ """
37
+ Project a point (x, y, z) onto a plane at z = 0
38
+
39
+ Parameters
40
+ ----------
41
+ x, y, z: coordinates of the point to be projected
42
+ d0: distance from the camera to the plane
43
+
44
+ Returns
45
+ -------
46
+ ret: projected coordinates
47
+ """
22
48
  d = np.sqrt(x**2 + y**2 + z**2)
23
- return x * y0 / y, z * y0 / y, d
49
+ return x * d0 / (d0 - z), y * d0 / (d0 - z), d
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: QuLab
3
- Version: 2.11.5
3
+ Version: 2.11.7
4
4
  Summary: contral instruments and manage data
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
@@ -2,7 +2,7 @@ qulab/__init__.py,sha256=hmf6R3jiM5NtJY1mSptogYnH5DMTR2dTzlXykqLxQzg,2027
2
2
  qulab/__main__.py,sha256=fjaRSL_uUjNIzBGNgjlGswb9TJ2VD5qnkZHW3hItrD4,68
3
3
  qulab/typing.py,sha256=vg62sGqxuD9CI5677ejlzAmf2fVdAESZCQjAE_xSxPg,69
4
4
  qulab/utils.py,sha256=w5oSw9Ypux6l0oB-MzlQvT40iX_rzQ5wXcwzr7CIVf8,4107
5
- qulab/version.py,sha256=fNipWoN2EmgidscGxW-j_-B8q7t8BEt85UV6DPo_yRA,22
5
+ qulab/version.py,sha256=H2A8gHyN0Jg2EL7bDXRWVKNMgM1isfEU_bBVahUV62U,22
6
6
  qulab/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  qulab/cli/commands.py,sha256=ywKmwbGNBCVASx8OsgrRttaLz9ogwY38ZdKj7I-tdJ4,813
8
8
  qulab/cli/config.py,sha256=tZPSBLbf2x_Brb2UBuA1bsni8nC8WOPPGyWIi8m7j1I,5459
@@ -91,14 +91,14 @@ qulab/visualization/__init__.py,sha256=26cuHt3QIJXUb3VaMxlJx3IQTOUVJFKlYBZr7WMP5
91
91
  qulab/visualization/__main__.py,sha256=9zKK3yZFy0leU40ou6BpRC1Fsetfc1gjjFzIZYIwP6Y,1639
92
92
  qulab/visualization/_autoplot.py,sha256=31B7pn1pK19abpQDGYBU9a_27cDL87LBpx9vKqlcYAo,14165
93
93
  qulab/visualization/plot_circ.py,sha256=qZS3auNG4qIyUvC-ijTce17xJwGBvpRvPUqPEx64KUY,8759
94
- qulab/visualization/plot_layout.py,sha256=mfdKJUyjTNB3YyD6GcqM38GEwiae4g6Us9HJyAwen6Q,13535
94
+ qulab/visualization/plot_layout.py,sha256=n9yEmCOl7RJw2l0hXXRVsnLfDOMlK7A52kQZG_Y_NgM,17630
95
95
  qulab/visualization/plot_seq.py,sha256=UWTS6p9nfX_7B8ehcYo6UnSTUCjkBsNU9jiOeW2calY,6751
96
96
  qulab/visualization/qdat.py,sha256=ZeevBYWkzbww4xZnsjHhw7wRorJCBzbG0iEu-XQB4EA,5735
97
- qulab/visualization/rot3d.py,sha256=lMrEJlRLwYe6NMBlGkKYpp_V9CTipOAuDy6QW_cQK00,734
97
+ qulab/visualization/rot3d.py,sha256=CuLfG1jw1032HNNZxzC0OWtNzKsbj2e2WRYhMWDgvMQ,1321
98
98
  qulab/visualization/widgets.py,sha256=6KkiTyQ8J-ei70LbPQZAK35wjktY47w2IveOa682ftA,3180
99
- qulab-2.11.5.dist-info/licenses/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
100
- qulab-2.11.5.dist-info/METADATA,sha256=HUf9_g4vh9bEiGfmG0uO8WcL10bnkUvw5DfveaLro4w,3896
101
- qulab-2.11.5.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
102
- qulab-2.11.5.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
103
- qulab-2.11.5.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
104
- qulab-2.11.5.dist-info/RECORD,,
99
+ qulab-2.11.7.dist-info/licenses/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
100
+ qulab-2.11.7.dist-info/METADATA,sha256=XSG-tpZgI0O7k5DAGckiZSgu9QjgIvRU4h1cVO4oSqc,3896
101
+ qulab-2.11.7.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
102
+ qulab-2.11.7.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
103
+ qulab-2.11.7.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
104
+ qulab-2.11.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5