QuLab 2.11.6__py3-none-any.whl → 2.11.8__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/executor/cli.py CHANGED
@@ -1,7 +1,9 @@
1
+ import ast
1
2
  import functools
2
3
  import graphlib
3
4
  import importlib
4
5
  import os
6
+ import re
5
7
  import sys
6
8
  from pathlib import Path
7
9
 
@@ -283,7 +285,49 @@ def boot(bootstrap):
283
285
  run_script(bootstrap)
284
286
 
285
287
 
286
- @click.command()
288
+ def parse_dynamic_option_value(value):
289
+ """解析动态参数值"""
290
+ try:
291
+ parsed_value = ast.literal_eval(value)
292
+ except (ValueError, SyntaxError):
293
+ # 如果解析失败,返回原始字符串
294
+ parsed_value = value
295
+ return parsed_value
296
+
297
+
298
+ def parse_dynamic_options(args):
299
+ """解析格式为 key=value 的未知参数列表"""
300
+ pattern = re.compile(r'^([a-zA-Z_][\w\-]*)=(.+)$')
301
+ result = {}
302
+ for arg in args:
303
+ match = pattern.match(arg)
304
+ if match:
305
+ key, value = match.groups()
306
+ result[key] = parse_dynamic_option_value(value)
307
+ else:
308
+ raise ValueError(
309
+ f"Invalid argument format: {arg}. Expected format is key=value."
310
+ )
311
+ return result
312
+
313
+
314
+ help_doc = """
315
+ Run a workflow.
316
+
317
+ If the workflow has entries, run all entries.
318
+ If `--no-dependents` is set, only run the workflow itself.
319
+ If `--retry` is set, retry the workflow when calibration failed.
320
+ If `--freeze` is set, freeze the config table.
321
+ If `--plot` is set, plot the report.
322
+ If `--api` is set, use the api to get and update the config table.
323
+ If `--code` is not set, use the current working directory.
324
+ If `--data` is not set, use the `logs` directory in the code path.
325
+ """
326
+
327
+
328
+ @click.command(context_settings=dict(ignore_unknown_options=True,
329
+ allow_extra_args=True),
330
+ help=help_doc)
287
331
  @click.argument('workflow')
288
332
  @click.option('--plot', '-p', is_flag=True, help='Plot the report.')
289
333
  @click.option('--no-dependents',
@@ -301,8 +345,10 @@ def boot(bootstrap):
301
345
  help='Veryfy the source code.')
302
346
  @log_options('run')
303
347
  @command_option('run')
348
+ @click.pass_context
304
349
  @async_command
305
- async def run(workflow,
350
+ async def run(ctx,
351
+ workflow,
306
352
  code,
307
353
  data,
308
354
  api,
@@ -312,18 +358,6 @@ async def run(workflow,
312
358
  freeze,
313
359
  fail_fast,
314
360
  veryfy_source_code=True):
315
- """
316
- Run a workflow.
317
-
318
- If the workflow has entries, run all entries.
319
- If `--no-dependents` is set, only run the workflow itself.
320
- If `--retry` is set, retry the workflow when calibration failed.
321
- If `--freeze` is set, freeze the config table.
322
- If `--plot` is set, plot the report.
323
- If `--api` is set, use the api to get and update the config table.
324
- If `--code` is not set, use the current working directory.
325
- If `--data` is not set, use the `logs` directory in the code path.
326
- """
327
361
  logger.info(
328
362
  f'[CMD]: run {workflow} --code {code} --data {data} --api {api}'
329
363
  f'{" --plot" if plot else ""}'
@@ -342,6 +376,13 @@ async def run(workflow,
342
376
  code = Path(os.path.expanduser(code))
343
377
  data = Path(os.path.expanduser(data))
344
378
 
379
+ extra_args = ctx.args
380
+ params = parse_dynamic_options(extra_args)
381
+
382
+ if params:
383
+ workflow = (workflow, params)
384
+ rich.print(workflow)
385
+
345
386
  wf = load_workflow(workflow, code, veryfy_source_code=veryfy_source_code)
346
387
  check_toplogy(wf, code, veryfy_source_code=veryfy_source_code)
347
388
 
qulab/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2.11.6"
1
+ __version__ = "2.11.8"
@@ -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': {
@@ -152,7 +154,7 @@ def circle_path(pos, r, n=40):
152
154
  return xx, yy
153
155
 
154
156
 
155
- def circle_link_path(pos1, pos2, r1, r2, width, n=20):
157
+ def circle_link_path(pos1, pos2, r1, r2, width, n=2):
156
158
  width = min(2 * max(r1, r2), width)
157
159
 
158
160
  x1, y1 = pos1
@@ -204,59 +206,185 @@ def circle_half_directed_link_path(pos1, pos2, r1, r2, width, n=20):
204
206
  xx2[:-1]]), np.hstack([yy2[-1], yy1, a.imag, yy2[:-1]])
205
207
 
206
208
 
207
- 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
+ """
208
349
  if ax is None:
209
350
  ax = plt.gca()
210
351
 
211
- for qubit in layout['qubits'].values():
212
- pos = qubit['pos']
213
- if origin == 'upper':
214
- pos = pos[0], -pos[1]
215
- path = circle_path(pos, qubit.get('radius', 0.5))
216
- plot_range(ax,
217
- path,
218
- qubit.get('text', ''),
219
- qubit.get('color', None),
220
- lw=qubit.get('lw', 0.5),
221
- fontsize=qubit.get('fontsize', 9),
222
- rotation=qubit.get('rotation', 0),
223
- text_color=qubit.get('text_color', 'k'),
224
- bounder_color=qubit.get('bounder_color', 'k'))
225
-
226
- for coupler in layout['couplers'].values():
227
- q1, q2 = coupler['qubits']
228
- pos1 = layout['qubits'][q1]['pos']
229
- pos2 = layout['qubits'][q2]['pos']
230
- if origin == 'upper':
231
- pos1 = pos1[0], -pos1[1]
232
- pos2 = pos2[0], -pos2[1]
233
- r1 = layout['qubits'][q1].get('radius', 0.5)
234
- r2 = layout['qubits'][q2].get('radius', 0.5)
235
- width = coupler.get('width', 0.5)
236
- lw = coupler.get('lw', 0.5)
237
-
238
- rotation = 180 * np.arctan2(pos2[1] - pos1[1],
239
- pos2[0] - pos1[0]) / np.pi
240
-
241
- path = circle_link_path(pos1, pos2, r1, r2, width)
242
- plot_range(ax,
243
- path,
244
- coupler.get('text', ''),
245
- color=coupler.get('color', None),
246
- lw=0,
247
- fontsize=coupler.get('fontsize', 9),
248
- rotation=coupler.get('rotation', rotation),
249
- text_color=coupler.get('text_color', 'k'))
250
- if lw > 0:
251
- x, y = circle_link_path(pos1, pos2, r1, r2, width, n=2)
252
- ax.plot(x[:2],
253
- y[:2],
254
- lw=lw,
255
- color=coupler.get('bounder_color', 'k'))
256
- ax.plot(x[2:],
257
- y[2:],
258
- lw=lw,
259
- 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
260
388
 
261
389
  ax.axis('equal')
262
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.6
3
+ Version: 2.11.8
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,14 +2,14 @@ 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=Bx8wB0SH56T33xWJQ2wPqk64pcd9l8Qx4gqE_grdxI4,22
5
+ qulab/version.py,sha256=LePXLjmUKQGA1V4KFVXMXBUtYne8QBdsbxVF-1ZmK4k,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
9
9
  qulab/cli/decorators.py,sha256=oImteZVnDPPWdyhJ4kzf2KYGJLON7VsKGBvZadWLQZo,621
10
10
  qulab/executor/__init__.py,sha256=LosPzOMaljSZY1thy_Fxtbrgq7uubJszMABEB7oM7tU,101
11
11
  qulab/executor/analyze.py,sha256=4Hau5LrKUdpweh7W94tcG4ahgxucHOevbM0hm57T7zE,5649
12
- qulab/executor/cli.py,sha256=ToFVaLnCHCxlpJQG9_LF1XIdPUJyfI_zx2C3lk3s9OA,18727
12
+ qulab/executor/cli.py,sha256=iAyb9_w8r2zWETIwr5xUv3D_5mAVx3DCWcw6jVJaBPE,19862
13
13
  qulab/executor/load.py,sha256=eMlzOrrn8GpbP3J3uY5JJ8iO7tL5B1DWP1z2qiGyNhU,20385
14
14
  qulab/executor/registry.py,sha256=gym9F5FIDY5eV-cSCZsP99wC4l-6jkx9VMjJMaPOLaQ,4730
15
15
  qulab/executor/schedule.py,sha256=7gAJFwj13j1niGjVa1fSzwOS22eNFEN1hdrN3dfTY6A,21410
@@ -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=FaE3I1VgY2dO5wNw_jRMwiVnUC6lHT3C0Wa7RJCUvVI,13833
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.6.dist-info/licenses/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
100
- qulab-2.11.6.dist-info/METADATA,sha256=SXXw-hAwWhB0ErJKBJX9RMx9MF4-PIXdSV6jvnKlzyk,3896
101
- qulab-2.11.6.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
102
- qulab-2.11.6.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
103
- qulab-2.11.6.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
104
- qulab-2.11.6.dist-info/RECORD,,
99
+ qulab-2.11.8.dist-info/licenses/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
100
+ qulab-2.11.8.dist-info/METADATA,sha256=3_BeI_HmiV3Rp005N9KSCc6GpUuTKRhI6u3oUdCk850,3896
101
+ qulab-2.11.8.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
102
+ qulab-2.11.8.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
103
+ qulab-2.11.8.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
104
+ qulab-2.11.8.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