QuLab 2.10.0__cp311-cp311-win_amd64.whl → 2.10.2__cp311-cp311-win_amd64.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 +104 -46
- qulab/executor/load.py +71 -34
- qulab/executor/schedule.py +46 -22
- qulab/executor/utils.py +7 -7
- qulab/fun.cp311-win_amd64.pyd +0 -0
- qulab/sys/device/basedevice.py +24 -8
- qulab/sys/drivers/FakeInstrument.py +5 -5
- qulab/version.py +1 -1
- {qulab-2.10.0.dist-info → qulab-2.10.2.dist-info}/METADATA +1 -1
- {qulab-2.10.0.dist-info → qulab-2.10.2.dist-info}/RECORD +14 -14
- {qulab-2.10.0.dist-info → qulab-2.10.2.dist-info}/WHEEL +0 -0
- {qulab-2.10.0.dist-info → qulab-2.10.2.dist-info}/entry_points.txt +0 -0
- {qulab-2.10.0.dist-info → qulab-2.10.2.dist-info}/licenses/LICENSE +0 -0
- {qulab-2.10.0.dist-info → qulab-2.10.2.dist-info}/top_level.txt +0 -0
qulab/executor/cli.py
CHANGED
@@ -30,11 +30,16 @@ def boot(script_path):
|
|
30
30
|
|
31
31
|
|
32
32
|
@logger.catch(reraise=True)
|
33
|
-
def check_toplogy(workflow: WorkflowType,
|
33
|
+
def check_toplogy(workflow: WorkflowType,
|
34
|
+
code_path: str | Path,
|
35
|
+
veryfy_source_code: bool = True) -> dict:
|
34
36
|
graph = {}
|
35
37
|
try:
|
36
|
-
graphlib.TopologicalSorter(
|
37
|
-
|
38
|
+
graphlib.TopologicalSorter(
|
39
|
+
make_graph(workflow,
|
40
|
+
graph,
|
41
|
+
code_path,
|
42
|
+
veryfy_source_code=veryfy_source_code)).static_order()
|
38
43
|
except graphlib.CycleError as e:
|
39
44
|
logger.error(
|
40
45
|
f"Workflow {workflow.__workflow_id__} has a circular dependency: {e}"
|
@@ -167,7 +172,15 @@ def get(key, api):
|
|
167
172
|
@log_options
|
168
173
|
@command_option('run')
|
169
174
|
@async_command
|
170
|
-
async def run(workflow,
|
175
|
+
async def run(workflow,
|
176
|
+
code,
|
177
|
+
data,
|
178
|
+
api,
|
179
|
+
plot,
|
180
|
+
no_dependents,
|
181
|
+
retry,
|
182
|
+
freeze,
|
183
|
+
veryfy_source_code=True):
|
171
184
|
"""
|
172
185
|
Run a workflow.
|
173
186
|
|
@@ -198,41 +211,67 @@ async def run(workflow, code, data, api, plot, no_dependents, retry, freeze):
|
|
198
211
|
code = Path(os.path.expanduser(code))
|
199
212
|
data = Path(os.path.expanduser(data))
|
200
213
|
|
201
|
-
wf = load_workflow(workflow, code)
|
202
|
-
check_toplogy(wf, code)
|
214
|
+
wf = load_workflow(workflow, code, veryfy_source_code=veryfy_source_code)
|
215
|
+
check_toplogy(wf, code, veryfy_source_code=veryfy_source_code)
|
203
216
|
|
204
217
|
for i in range(retry):
|
205
218
|
try:
|
206
219
|
if no_dependents:
|
207
220
|
if hasattr(wf, 'entries'):
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
221
|
+
exceptions = []
|
222
|
+
for entry in get_entries(
|
223
|
+
wf, code, veryfy_source_code=veryfy_source_code):
|
224
|
+
try:
|
225
|
+
await run_workflow(
|
226
|
+
entry,
|
227
|
+
code,
|
228
|
+
data,
|
229
|
+
plot=plot,
|
230
|
+
freeze=freeze,
|
231
|
+
veryfy_source_code=veryfy_source_code,
|
232
|
+
)
|
233
|
+
except Exception as e:
|
234
|
+
exceptions.append(e)
|
235
|
+
if any(exceptions):
|
236
|
+
raise exceptions[0]
|
214
237
|
else:
|
215
|
-
await run_workflow(
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
238
|
+
await run_workflow(
|
239
|
+
wf,
|
240
|
+
code,
|
241
|
+
data,
|
242
|
+
plot=plot,
|
243
|
+
freeze=freeze,
|
244
|
+
veryfy_source_code=veryfy_source_code,
|
245
|
+
)
|
220
246
|
else:
|
221
247
|
if hasattr(wf, 'entries'):
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
248
|
+
exceptions = []
|
249
|
+
for entry in get_entries(
|
250
|
+
wf, code, veryfy_source_code=veryfy_source_code):
|
251
|
+
try:
|
252
|
+
await maintain_workflow(
|
253
|
+
entry,
|
254
|
+
code,
|
255
|
+
data,
|
256
|
+
run=True,
|
257
|
+
plot=plot,
|
258
|
+
freeze=freeze,
|
259
|
+
veryfy_source_code=veryfy_source_code,
|
260
|
+
)
|
261
|
+
except Exception as e:
|
262
|
+
exceptions.append(e)
|
263
|
+
if any(exceptions):
|
264
|
+
raise exceptions[0]
|
229
265
|
else:
|
230
|
-
await maintain_workflow(
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
266
|
+
await maintain_workflow(
|
267
|
+
wf,
|
268
|
+
code,
|
269
|
+
data,
|
270
|
+
run=True,
|
271
|
+
plot=plot,
|
272
|
+
freeze=freeze,
|
273
|
+
veryfy_source_code=veryfy_source_code,
|
274
|
+
)
|
236
275
|
break
|
237
276
|
except CalibrationFailedError as e:
|
238
277
|
if i == retry - 1:
|
@@ -248,7 +287,13 @@ async def run(workflow, code, data, api, plot, no_dependents, retry, freeze):
|
|
248
287
|
@log_options
|
249
288
|
@command_option('maintain')
|
250
289
|
@async_command
|
251
|
-
async def maintain(workflow,
|
290
|
+
async def maintain(workflow,
|
291
|
+
code,
|
292
|
+
data,
|
293
|
+
api,
|
294
|
+
retry,
|
295
|
+
plot,
|
296
|
+
veryfy_source_code=True):
|
252
297
|
"""
|
253
298
|
Maintain a workflow.
|
254
299
|
|
@@ -275,26 +320,39 @@ async def maintain(workflow, code, data, api, retry, plot):
|
|
275
320
|
code = Path(os.path.expanduser(code))
|
276
321
|
data = Path(os.path.expanduser(data))
|
277
322
|
|
278
|
-
wf = load_workflow(workflow, code)
|
279
|
-
check_toplogy(wf, code)
|
323
|
+
wf = load_workflow(workflow, code, veryfy_source_code=veryfy_source_code)
|
324
|
+
check_toplogy(wf, code, veryfy_source_code=veryfy_source_code)
|
280
325
|
|
281
326
|
for i in range(retry):
|
282
327
|
try:
|
283
328
|
if hasattr(wf, 'entries'):
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
329
|
+
exceptions = []
|
330
|
+
for entry in get_entries(
|
331
|
+
wf, code, veryfy_source_code=veryfy_source_code):
|
332
|
+
try:
|
333
|
+
await maintain_workflow(
|
334
|
+
entry,
|
335
|
+
code,
|
336
|
+
data,
|
337
|
+
run=False,
|
338
|
+
plot=plot,
|
339
|
+
freeze=False,
|
340
|
+
veryfy_source_code=veryfy_source_code,
|
341
|
+
)
|
342
|
+
except Exception as e:
|
343
|
+
exceptions.append(e)
|
344
|
+
if any(exceptions):
|
345
|
+
raise exceptions[0]
|
291
346
|
else:
|
292
|
-
await maintain_workflow(
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
347
|
+
await maintain_workflow(
|
348
|
+
wf,
|
349
|
+
code,
|
350
|
+
data,
|
351
|
+
run=False,
|
352
|
+
plot=plot,
|
353
|
+
freeze=False,
|
354
|
+
veryfy_source_code=veryfy_source_code,
|
355
|
+
)
|
298
356
|
break
|
299
357
|
except CalibrationFailedError as e:
|
300
358
|
if i == retry - 1:
|
qulab/executor/load.py
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
import atexit
|
2
2
|
import inspect
|
3
3
|
import pickle
|
4
|
+
import os
|
5
|
+
import time
|
6
|
+
import shutil
|
4
7
|
import tempfile
|
5
8
|
import warnings
|
6
9
|
from importlib.util import module_from_spec, spec_from_file_location
|
@@ -135,10 +138,14 @@ def verify_dependence_key(workflow: str | tuple[str, dict[str, Any]]
|
|
135
138
|
if not isinstance(workflow, tuple) or len(workflow) not in [2, 3]:
|
136
139
|
raise ValueError(f"Invalid workflow: {workflow}")
|
137
140
|
|
138
|
-
if len(workflow) == 2:
|
141
|
+
if len(workflow) == 2 and isinstance(workflow[1], str):
|
139
142
|
file_name, mapping = workflow
|
140
143
|
if not Path(file_name).exists():
|
141
144
|
raise FileNotFoundError(f"File not found: {file_name}")
|
145
|
+
elif len(workflow) == 2 and isinstance(workflow[1], dict):
|
146
|
+
template_path, mapping = workflow
|
147
|
+
if not (Path(base_path) / template_path).exists():
|
148
|
+
raise FileNotFoundError(f"File not found: {template_path}")
|
142
149
|
elif len(workflow) == 3:
|
143
150
|
template_path, target_path, mapping = workflow
|
144
151
|
if not (Path(base_path) / template_path).exists():
|
@@ -301,7 +308,8 @@ def find_unreferenced_workflows(path: str) -> list[str]:
|
|
301
308
|
|
302
309
|
def load_workflow_from_file(file_name: str,
|
303
310
|
base_path: str | Path,
|
304
|
-
package='workflows'
|
311
|
+
package='workflows',
|
312
|
+
veryfy_source_code: bool = True) -> WorkflowType:
|
305
313
|
base_path = Path(base_path)
|
306
314
|
path = Path(file_name)
|
307
315
|
if not (base_path / path).exists():
|
@@ -315,7 +323,8 @@ def load_workflow_from_file(file_name: str,
|
|
315
323
|
module.__source__ = source_code
|
316
324
|
|
317
325
|
if hasattr(module, 'entries'):
|
318
|
-
|
326
|
+
if veryfy_source_code:
|
327
|
+
verify_entries(module, base_path)
|
319
328
|
return module
|
320
329
|
|
321
330
|
if not hasattr(module, '__timeout__'):
|
@@ -323,9 +332,11 @@ def load_workflow_from_file(file_name: str,
|
|
323
332
|
|
324
333
|
if not hasattr(module, 'depends'):
|
325
334
|
module.depends = lambda: []
|
326
|
-
|
327
|
-
|
328
|
-
|
335
|
+
|
336
|
+
if veryfy_source_code:
|
337
|
+
verify_depends(module, base_path)
|
338
|
+
verify_calibrate_method(module)
|
339
|
+
verify_check_method(module)
|
329
340
|
|
330
341
|
return module
|
331
342
|
|
@@ -376,12 +387,14 @@ def _generate_target_file_path(template_path: str | Path, hash_str: str,
|
|
376
387
|
return Path('run') / path
|
377
388
|
|
378
389
|
|
379
|
-
def load_workflow_from_template(
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
390
|
+
def load_workflow_from_template(
|
391
|
+
template_path: str,
|
392
|
+
mapping: dict[str, str],
|
393
|
+
base_path: str | Path,
|
394
|
+
target_path: str | None = None,
|
395
|
+
package='workflows',
|
396
|
+
mtime: float = 0,
|
397
|
+
veryfy_source_code: bool = True) -> WorkflowType:
|
385
398
|
base_path = Path(base_path)
|
386
399
|
path = Path(template_path)
|
387
400
|
|
@@ -409,7 +422,8 @@ def load_workflow_from_template(template_path: str,
|
|
409
422
|
f"`{file}` already exists and is different from the new one generated from template `{template_path}`"
|
410
423
|
)
|
411
424
|
|
412
|
-
module = load_workflow_from_file(str(path), base_path, package
|
425
|
+
module = load_workflow_from_file(str(path), base_path, package,
|
426
|
+
veryfy_source_code)
|
413
427
|
module.__mtime__ = max(mtime, module.__mtime__)
|
414
428
|
if module.__source__ == content:
|
415
429
|
module.__source__ = template, mapping, str(template_path)
|
@@ -421,26 +435,31 @@ def load_workflow(workflow: str | tuple[str, dict],
|
|
421
435
|
base_path: str | Path,
|
422
436
|
package='workflows',
|
423
437
|
mtime: float = 0,
|
424
|
-
inject: dict | None = None
|
438
|
+
inject: dict | None = None,
|
439
|
+
veryfy_source_code: bool = True) -> WorkflowType:
|
425
440
|
if isinstance(workflow, tuple):
|
426
441
|
if len(workflow) == 2:
|
427
442
|
file_name, mapping = workflow
|
428
443
|
if inject is None:
|
429
444
|
w = load_workflow_from_template(file_name, mapping, base_path,
|
430
|
-
None, package, mtime
|
445
|
+
None, package, mtime,
|
446
|
+
veryfy_source_code)
|
431
447
|
else:
|
432
448
|
w = load_workflow_from_template(file_name, inject, base_path,
|
433
|
-
None, package, mtime
|
449
|
+
None, package, mtime,
|
450
|
+
veryfy_source_code)
|
434
451
|
elif len(workflow) == 3:
|
435
452
|
template_path, target_path, mapping = workflow
|
436
453
|
if inject is None:
|
437
454
|
w = load_workflow_from_template(template_path, mapping,
|
438
455
|
base_path, target_path,
|
439
|
-
package, mtime
|
456
|
+
package, mtime,
|
457
|
+
veryfy_source_code)
|
440
458
|
else:
|
441
459
|
w = load_workflow_from_template(template_path, inject,
|
442
460
|
base_path, target_path,
|
443
|
-
package, mtime
|
461
|
+
package, mtime,
|
462
|
+
veryfy_source_code)
|
444
463
|
else:
|
445
464
|
raise ValueError(f"Invalid workflow: {workflow}")
|
446
465
|
w.__workflow_id__ = str(Path(w.__file__).relative_to(base_path))
|
@@ -450,7 +469,8 @@ def load_workflow(workflow: str | tuple[str, dict],
|
|
450
469
|
w = SetConfigWorkflow(key)
|
451
470
|
w.__workflow_id__ = workflow
|
452
471
|
else:
|
453
|
-
w = load_workflow_from_file(workflow, base_path, package
|
472
|
+
w = load_workflow_from_file(workflow, base_path, package,
|
473
|
+
veryfy_source_code)
|
454
474
|
w.__workflow_id__ = str(Path(w.__file__).relative_to(base_path))
|
455
475
|
else:
|
456
476
|
raise TypeError(f"Invalid workflow: {workflow}")
|
@@ -458,11 +478,15 @@ def load_workflow(workflow: str | tuple[str, dict],
|
|
458
478
|
return w
|
459
479
|
|
460
480
|
|
461
|
-
def _load_workflow_list(workflow, lst, code_path):
|
481
|
+
def _load_workflow_list(workflow, lst, code_path, veryfy_source_code):
|
462
482
|
ret = []
|
463
483
|
for i, n in enumerate(lst):
|
464
484
|
try:
|
465
|
-
ret.append(
|
485
|
+
ret.append(
|
486
|
+
load_workflow(n,
|
487
|
+
code_path,
|
488
|
+
mtime=workflow.__mtime__,
|
489
|
+
veryfy_source_code=veryfy_source_code))
|
466
490
|
except TemplateKeyError as e:
|
467
491
|
msg, *_ = e.args
|
468
492
|
e.args = (
|
@@ -471,16 +495,18 @@ def _load_workflow_list(workflow, lst, code_path):
|
|
471
495
|
return ret
|
472
496
|
|
473
497
|
|
474
|
-
def get_dependents(workflow: WorkflowType,
|
475
|
-
|
498
|
+
def get_dependents(workflow: WorkflowType, code_path: str | Path,
|
499
|
+
veryfy_source_code: bool) -> list[WorkflowType]:
|
476
500
|
if callable(getattr(workflow, 'depends', None)):
|
477
501
|
if not can_call_without_args(workflow.depends):
|
478
502
|
raise AttributeError(
|
479
503
|
f'Workflow {workflow.__workflow_id__} "depends" function should not have any parameters'
|
480
504
|
)
|
481
|
-
return _load_workflow_list(workflow, workflow.depends(), code_path
|
505
|
+
return _load_workflow_list(workflow, workflow.depends(), code_path,
|
506
|
+
veryfy_source_code)
|
482
507
|
elif isinstance(getattr(workflow, 'depends', None), (list, tuple)):
|
483
|
-
return _load_workflow_list(workflow, workflow.depends, code_path
|
508
|
+
return _load_workflow_list(workflow, workflow.depends, code_path,
|
509
|
+
veryfy_source_code)
|
484
510
|
elif getattr(workflow, 'depends', None) is None:
|
485
511
|
return []
|
486
512
|
else:
|
@@ -489,16 +515,18 @@ def get_dependents(workflow: WorkflowType,
|
|
489
515
|
)
|
490
516
|
|
491
517
|
|
492
|
-
def get_entries(workflow: WorkflowType,
|
493
|
-
|
518
|
+
def get_entries(workflow: WorkflowType, code_path: str | Path,
|
519
|
+
veryfy_source_code: bool) -> list[WorkflowType]:
|
494
520
|
if callable(getattr(workflow, 'entries', None)):
|
495
521
|
if not can_call_without_args(workflow.entries):
|
496
522
|
raise AttributeError(
|
497
523
|
f'Workflow {workflow.__workflow_id__} "entries" function should not have any parameters'
|
498
524
|
)
|
499
|
-
return _load_workflow_list(workflow, workflow.entries(), code_path
|
525
|
+
return _load_workflow_list(workflow, workflow.entries(), code_path,
|
526
|
+
veryfy_source_code)
|
500
527
|
elif isinstance(getattr(workflow, 'entries', None), (list, tuple)):
|
501
|
-
return _load_workflow_list(workflow, workflow.entries, code_path
|
528
|
+
return _load_workflow_list(workflow, workflow.entries, code_path,
|
529
|
+
veryfy_source_code)
|
502
530
|
elif getattr(workflow, 'entries', None) is None:
|
503
531
|
return []
|
504
532
|
else:
|
@@ -507,19 +535,28 @@ def get_entries(workflow: WorkflowType,
|
|
507
535
|
)
|
508
536
|
|
509
537
|
|
510
|
-
def make_graph(workflow: WorkflowType,
|
538
|
+
def make_graph(workflow: WorkflowType,
|
539
|
+
graph: dict,
|
540
|
+
code_path: str | Path,
|
541
|
+
veryfy_source_code: bool = True):
|
511
542
|
if workflow.__workflow_id__ in graph:
|
512
543
|
return graph
|
513
544
|
graph[workflow.__workflow_id__] = []
|
514
545
|
|
515
546
|
if hasattr(workflow, 'entries'):
|
516
|
-
for w in get_entries(workflow, code_path):
|
547
|
+
for w in get_entries(workflow, code_path, veryfy_source_code):
|
517
548
|
graph[workflow.__workflow_id__].append(w.__workflow_id__)
|
518
|
-
make_graph(w,
|
549
|
+
make_graph(w,
|
550
|
+
graph=graph,
|
551
|
+
code_path=code_path,
|
552
|
+
veryfy_source_code=veryfy_source_code)
|
519
553
|
elif hasattr(workflow, 'depends'):
|
520
|
-
for w in get_dependents(workflow, code_path):
|
554
|
+
for w in get_dependents(workflow, code_path, veryfy_source_code):
|
521
555
|
graph[workflow.__workflow_id__].append(w.__workflow_id__)
|
522
|
-
make_graph(w,
|
556
|
+
make_graph(w,
|
557
|
+
graph=graph,
|
558
|
+
code_path=code_path,
|
559
|
+
veryfy_source_code=veryfy_source_code)
|
523
560
|
if graph[workflow.__workflow_id__] == []:
|
524
561
|
del graph[workflow.__workflow_id__]
|
525
562
|
|
qulab/executor/schedule.py
CHANGED
@@ -71,7 +71,7 @@ def veryfy_analyzed_report(report: Report, script: str, method: str):
|
|
71
71
|
|
72
72
|
|
73
73
|
def check_state(workflow: WorkflowType, code_path: str | Path,
|
74
|
-
state_path: str | Path) -> bool:
|
74
|
+
state_path: str | Path, veryfy_source_code: bool) -> bool:
|
75
75
|
"""
|
76
76
|
check state should report a pass if and only if the following are satisfied:
|
77
77
|
|
@@ -110,15 +110,15 @@ def check_state(workflow: WorkflowType, code_path: str | Path,
|
|
110
110
|
logger.debug(
|
111
111
|
f'check_state failed: "{workflow.__workflow_id__}" has bad data')
|
112
112
|
return False
|
113
|
-
for n in get_dependents(workflow, code_path):
|
113
|
+
for n in get_dependents(workflow, code_path, veryfy_source_code):
|
114
114
|
r = find_report(n.__workflow_id__, state_path)
|
115
115
|
if r is None or r.checked_time > report.checked_time:
|
116
116
|
logger.debug(
|
117
117
|
f'check_state failed: "{workflow.__workflow_id__}" has outdated dependencies'
|
118
118
|
)
|
119
119
|
return False
|
120
|
-
for n in get_dependents(workflow, code_path):
|
121
|
-
if not check_state(n, code_path, state_path):
|
120
|
+
for n in get_dependents(workflow, code_path, veryfy_source_code):
|
121
|
+
if not check_state(n, code_path, state_path, veryfy_source_code):
|
122
122
|
logger.debug(
|
123
123
|
f'check_state failed: "{workflow.__workflow_id__}" has bad dependencies'
|
124
124
|
)
|
@@ -126,7 +126,7 @@ def check_state(workflow: WorkflowType, code_path: str | Path,
|
|
126
126
|
return True
|
127
127
|
|
128
128
|
|
129
|
-
@logger.catch()
|
129
|
+
@logger.catch(reraise=False)
|
130
130
|
async def call_plot(node: WorkflowType, report: Report, check=False):
|
131
131
|
if hasattr(node, 'plot') and callable(node.plot):
|
132
132
|
if inspect.iscoroutinefunction(node.plot):
|
@@ -361,7 +361,8 @@ async def calibrate(workflow: WorkflowType, state_path: str | Path, plot: bool,
|
|
361
361
|
|
362
362
|
|
363
363
|
async def diagnose(workflow: WorkflowType, code_path: str | Path,
|
364
|
-
state_path: str | Path, plot: bool, session_id: str
|
364
|
+
state_path: str | Path, plot: bool, session_id: str,
|
365
|
+
veryfy_source_code: bool):
|
365
366
|
'''
|
366
367
|
Returns: True if node or dependent recalibrated.
|
367
368
|
'''
|
@@ -379,10 +380,17 @@ async def diagnose(workflow: WorkflowType, code_path: str | Path,
|
|
379
380
|
if report.bad_data:
|
380
381
|
logger.debug(
|
381
382
|
f'"{workflow.__workflow_id__}": Bad data, diagnosing dependents')
|
382
|
-
recalibrated = [
|
383
|
-
|
384
|
-
|
385
|
-
|
383
|
+
recalibrated = []
|
384
|
+
exceptions = []
|
385
|
+
for n in get_dependents(workflow, code_path, veryfy_source_code):
|
386
|
+
try:
|
387
|
+
flag = await diagnose(n, code_path, state_path, plot,
|
388
|
+
session_id, veryfy_source_code)
|
389
|
+
except Exception as e:
|
390
|
+
exceptions.append(e)
|
391
|
+
recalibrated.append(flag)
|
392
|
+
if any(exceptions):
|
393
|
+
raise exceptions[0]
|
386
394
|
if not any(recalibrated):
|
387
395
|
if report.bad_data:
|
388
396
|
raise CalibrationFailedError(
|
@@ -427,28 +435,37 @@ async def maintain(workflow: WorkflowType,
|
|
427
435
|
session_id: str | None = None,
|
428
436
|
run: bool = False,
|
429
437
|
plot: bool = False,
|
430
|
-
freeze: bool = False
|
438
|
+
freeze: bool = False,
|
439
|
+
veryfy_source_code: bool = True):
|
431
440
|
if session_id is None:
|
432
441
|
session_id = uuid.uuid4().hex
|
433
442
|
logger.debug(f'run "{workflow.__workflow_id__}"'
|
434
443
|
if run else f'maintain "{workflow.__workflow_id__}"')
|
435
444
|
# recursive maintain
|
436
|
-
|
445
|
+
exceptions = []
|
446
|
+
for n in get_dependents(workflow, code_path, veryfy_source_code):
|
437
447
|
logger.debug(
|
438
448
|
f'maintain "{n.__workflow_id__}" because it is depended by "{workflow.__workflow_id__}"'
|
439
449
|
)
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
450
|
+
try:
|
451
|
+
await maintain(n,
|
452
|
+
code_path,
|
453
|
+
state_path,
|
454
|
+
session_id,
|
455
|
+
run=False,
|
456
|
+
plot=plot,
|
457
|
+
freeze=freeze,
|
458
|
+
veryfy_source_code=veryfy_source_code)
|
459
|
+
except Exception as e:
|
460
|
+
exceptions.append(e)
|
447
461
|
else:
|
448
462
|
logger.debug(
|
449
463
|
f'"{workflow.__workflow_id__}": All dependents maintained')
|
464
|
+
if any(exceptions):
|
465
|
+
raise exceptions[0]
|
450
466
|
# check_state
|
451
|
-
if check_state(workflow, code_path, state_path
|
467
|
+
if check_state(workflow, code_path, state_path,
|
468
|
+
veryfy_source_code) and not run:
|
452
469
|
logger.debug(
|
453
470
|
f'"{workflow.__workflow_id__}": In spec, no need to maintain')
|
454
471
|
return
|
@@ -462,14 +479,21 @@ async def maintain(workflow: WorkflowType,
|
|
462
479
|
elif report.bad_data:
|
463
480
|
logger.debug(
|
464
481
|
f'"{workflow.__workflow_id__}": Bad data, diagnosing dependents')
|
465
|
-
|
482
|
+
exceptions = []
|
483
|
+
for n in get_dependents(workflow, code_path, veryfy_source_code):
|
466
484
|
logger.debug(
|
467
485
|
f'diagnose "{n.__workflow_id__}" because of "{workflow.__workflow_id__}" bad data'
|
468
486
|
)
|
469
|
-
|
487
|
+
try:
|
488
|
+
await diagnose(n, code_path, state_path, plot, session_id,
|
489
|
+
veryfy_source_code)
|
490
|
+
except Exception as e:
|
491
|
+
exceptions.append(e)
|
470
492
|
else:
|
471
493
|
logger.debug(
|
472
494
|
f'"{workflow.__workflow_id__}": All dependents diagnosed')
|
495
|
+
if any(exceptions):
|
496
|
+
raise exceptions[0]
|
473
497
|
# calibrate
|
474
498
|
logger.debug(f'recalibrate "{workflow.__workflow_id__}"')
|
475
499
|
report = await calibrate(workflow, state_path, plot, session_id)
|
qulab/executor/utils.py
CHANGED
@@ -33,11 +33,11 @@ def dependent_tree(node: str, code_path: str | Path) -> dict[str, list[str]]:
|
|
33
33
|
|
34
34
|
|
35
35
|
def workflow_template(workflow: str, deps: list[str]) -> str:
|
36
|
-
return f"""
|
37
|
-
|
36
|
+
return f"""
|
38
37
|
import numpy as np
|
39
38
|
from loguru import logger
|
40
39
|
|
40
|
+
from qulab import VAR
|
41
41
|
from qulab.typing import Report
|
42
42
|
|
43
43
|
|
@@ -48,7 +48,7 @@ def depends():
|
|
48
48
|
return {deps!r}
|
49
49
|
|
50
50
|
|
51
|
-
def calibrate():
|
51
|
+
async def calibrate():
|
52
52
|
logger.info(f"running {workflow} ...")
|
53
53
|
|
54
54
|
# calibrate 是一个完整的校准实验,如power Rabi,Ramsey等。
|
@@ -64,7 +64,7 @@ def calibrate():
|
|
64
64
|
return x, y
|
65
65
|
|
66
66
|
|
67
|
-
def analyze(report: Report, history: Report | None = None) -> Report:
|
67
|
+
async def analyze(report: Report, history: Report | None = None) -> Report:
|
68
68
|
\"\"\"
|
69
69
|
分析校准结果。
|
70
70
|
|
@@ -95,7 +95,7 @@ def analyze(report: Report, history: Report | None = None) -> Report:
|
|
95
95
|
return report
|
96
96
|
|
97
97
|
|
98
|
-
def check():
|
98
|
+
async def check():
|
99
99
|
logger.info(f"checking {workflow} ...")
|
100
100
|
|
101
101
|
# check 是一个快速检查实验,用于检查校准是否过时。
|
@@ -112,7 +112,7 @@ def check():
|
|
112
112
|
return x, y
|
113
113
|
|
114
114
|
|
115
|
-
def check_analyze(report: Report, history: Report | None = None) -> Report:
|
115
|
+
async def check_analyze(report: Report, history: Report | None = None) -> Report:
|
116
116
|
\"\"\"
|
117
117
|
分析检查结果。
|
118
118
|
|
@@ -136,7 +136,7 @@ def check_analyze(report: Report, history: Report | None = None) -> Report:
|
|
136
136
|
return report
|
137
137
|
|
138
138
|
|
139
|
-
def oracle(report: Report,
|
139
|
+
async def oracle(report: Report,
|
140
140
|
history: Report | None = None,
|
141
141
|
system_state: dict[str:str] | None = None) -> Report:
|
142
142
|
\"\"\"
|
qulab/fun.cp311-win_amd64.pyd
CHANGED
Binary file
|
qulab/sys/device/basedevice.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import copy
|
1
2
|
import itertools
|
2
3
|
import logging
|
3
4
|
import re
|
@@ -127,7 +128,8 @@ class BaseDevice(metaclass=DeviceMeta):
|
|
127
128
|
__log__ = None
|
128
129
|
|
129
130
|
def __init__(self, address: str = None, **options):
|
130
|
-
self.
|
131
|
+
self._state = {}
|
132
|
+
self._status = "NOT CONNECTED"
|
131
133
|
self.address = address
|
132
134
|
self.options = options
|
133
135
|
|
@@ -144,28 +146,40 @@ class BaseDevice(metaclass=DeviceMeta):
|
|
144
146
|
f"{self.__class__.__module__}.{self.__class__.__name__}")
|
145
147
|
return self.__log__
|
146
148
|
|
149
|
+
@property
|
150
|
+
def state(self):
|
151
|
+
return copy.deepcopy(self._state)
|
152
|
+
|
153
|
+
def get_state(self, key: str, default=None) -> Any:
|
154
|
+
return self._state.get(key, default)
|
155
|
+
|
156
|
+
@property
|
157
|
+
def status(self):
|
158
|
+
return self._status
|
159
|
+
|
147
160
|
def open(self) -> None:
|
148
|
-
|
161
|
+
self._status = "CONNECTED"
|
149
162
|
|
150
163
|
def close(self) -> None:
|
151
|
-
|
164
|
+
self._status = "NOT CONNECTED"
|
165
|
+
self._state.clear()
|
152
166
|
|
153
167
|
def reset(self) -> None:
|
154
|
-
self.
|
168
|
+
self._state.clear()
|
155
169
|
|
156
170
|
def get(self, key: str, default: Any = None) -> Any:
|
157
171
|
self.log.info(f'Get {key!r}')
|
158
172
|
if key in self.__get_actions__:
|
159
173
|
result = self.__get_actions__[key](self)
|
160
|
-
self.
|
174
|
+
self._state[key] = result
|
161
175
|
return result
|
162
176
|
else:
|
163
|
-
return self.
|
177
|
+
return self._state.get(key, default)
|
164
178
|
|
165
179
|
def set(self, key: str, value: Any = None) -> None:
|
166
180
|
self.log.info(f'Set {key!r} = {value!r}')
|
167
181
|
self.__set_actions__[key](self, value)
|
168
|
-
self.
|
182
|
+
self._state[key] = value
|
169
183
|
|
170
184
|
def post(self, key: str, value: Any = None) -> Any:
|
171
185
|
self.log.info(f'Post {key!r} = {value!r}')
|
@@ -174,7 +188,7 @@ class BaseDevice(metaclass=DeviceMeta):
|
|
174
188
|
def delete(self, key: str) -> None:
|
175
189
|
self.log.info(f'Delete {key!r}')
|
176
190
|
self.__delete_actions__[key](self)
|
177
|
-
del self.
|
191
|
+
del self._state[key]
|
178
192
|
|
179
193
|
def __repr__(self) -> str:
|
180
194
|
return f'{self.__class__.__name__}({self.address!r})'
|
@@ -193,8 +207,10 @@ class VisaDevice(BaseDevice):
|
|
193
207
|
rm = pyvisa.ResourceManager()
|
194
208
|
self.resource = rm.open_resource(self.address, **kwds)
|
195
209
|
self.errors = []
|
210
|
+
super().open()
|
196
211
|
|
197
212
|
def close(self) -> None:
|
213
|
+
super().close()
|
198
214
|
self.resource.close()
|
199
215
|
|
200
216
|
def reset(self) -> None:
|
@@ -35,7 +35,7 @@ class Device(BaseDevice):
|
|
35
35
|
|
36
36
|
# @get('${channel}.Vpp', channel=exclude(['M1', 'M2']))
|
37
37
|
# def get_voltage(self, channel: str, default=0.0) -> float:
|
38
|
-
# return self.
|
38
|
+
# return self._state.get(f'{channel}.Vpp', default)
|
39
39
|
|
40
40
|
@set('${channel}.Offset', channel=exclude(['M1', 'M2']))
|
41
41
|
def set_offset(
|
@@ -47,7 +47,7 @@ class Device(BaseDevice):
|
|
47
47
|
|
48
48
|
# @get('${channel}.Offset', channel=exclude(['M1', 'M2']))
|
49
49
|
# def get_offset(self, channel: str, default=0.0) -> float:
|
50
|
-
# return self.
|
50
|
+
# return self._state.get(f'{channel}.Offset', default)
|
51
51
|
|
52
52
|
@set('${channel}.Waveform', channel=exclude(['M1', 'M2']))
|
53
53
|
def set_waveform(self, value, channel: str) -> None:
|
@@ -55,7 +55,7 @@ class Device(BaseDevice):
|
|
55
55
|
|
56
56
|
# @get('${channel}.Waveform', channel=exclude(['M1', 'M2']))
|
57
57
|
# def get_waveform(self, channel: str, default=None) -> str:
|
58
|
-
# return self.
|
58
|
+
# return self._state.get(f'{channel}.Waveform', default)
|
59
59
|
|
60
60
|
@set('${channel}.Size', channel=['M1', 'M2'])
|
61
61
|
def set_size(self, value, channel: str) -> None:
|
@@ -63,6 +63,6 @@ class Device(BaseDevice):
|
|
63
63
|
|
64
64
|
@get('${channel}.Trace', channel=['M1', 'M2'])
|
65
65
|
def get_random_data(self, channel: str) -> np.ndarray:
|
66
|
-
size = self.
|
67
|
-
shots = self.
|
66
|
+
size = self._state.get(f'{channel}.Size', 1024)
|
67
|
+
shots = self._state.get(f'{channel}.Shots', 128)
|
68
68
|
return np.random.randn(shots, size)
|
qulab/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "2.10.
|
1
|
+
__version__ = "2.10.2"
|
@@ -1,23 +1,23 @@
|
|
1
1
|
qulab/__init__.py,sha256=ulapyzt9DsDgT68EEbzuhniofHb512pNTFc-Sxfrr8Y,375
|
2
2
|
qulab/__main__.py,sha256=FL4YsGZL1jEtmcPc5WbleArzhOHLMsWl7OH3O-1d1ss,72
|
3
3
|
qulab/dicttree.py,sha256=ZoSJVWK4VMqfzj42gPb_n5RqLlM6K1Me0WmLIfLEYf8,14195
|
4
|
-
qulab/fun.cp311-win_amd64.pyd,sha256=
|
4
|
+
qulab/fun.cp311-win_amd64.pyd,sha256=aOPP4p3-rFr4abiAORysVYu_lwKV4AvueFffjzHzQfA,31744
|
5
5
|
qulab/typing.py,sha256=PRtwbCHWY2ROKK8GHq4Bo8llXrIGo6xC73DrQf7S9os,71
|
6
6
|
qulab/utils.py,sha256=65N2Xj7kqRsQ4epoLNY6tL-i5ts6Wk8YuJYee3Te6zI,3077
|
7
|
-
qulab/version.py,sha256
|
7
|
+
qulab/version.py,sha256=UINbgWwinYgAg8jG5YWakGzXmyfjSZZfKyqiqfovSAk,22
|
8
8
|
qulab/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
qulab/cli/commands.py,sha256=ZTs32yQjvwPIsFjXYWNr4KqEvly0NISIqyh--96qCAY,627
|
10
10
|
qulab/cli/config.py,sha256=A3UnyaRtiofpokyzYkJnUdwzcsYX7H-xZvBVduIOSdg,5617
|
11
11
|
qulab/cli/decorators.py,sha256=UmJab1o-XH-8DVS_LZFGY2SPjyVgZ1QrJwkauTZDUik,626
|
12
12
|
qulab/executor/__init__.py,sha256=LosPzOMaljSZY1thy_Fxtbrgq7uubJszMABEB7oM7tU,101
|
13
13
|
qulab/executor/analyze.py,sha256=VoSthE2RTarY6Wj3QFKh4FqReMYL7djSAyuitgHs5K0,5837
|
14
|
-
qulab/executor/cli.py,sha256=
|
15
|
-
qulab/executor/load.py,sha256=
|
16
|
-
qulab/executor/schedule.py,sha256=
|
14
|
+
qulab/executor/cli.py,sha256=wfz2zl0-DeMMHvF2kqTzT8MGA-K2kIfZjRAByYsh33A,14052
|
15
|
+
qulab/executor/load.py,sha256=0-EtO4dkP6jB1vsgQlniv-OtPH1AZLjbtuvaWHsgbPs,20335
|
16
|
+
qulab/executor/schedule.py,sha256=GhY4-kkNKF_EQ45Gwhd358jkEqweSJGWcuKoWGRM2wY,21067
|
17
17
|
qulab/executor/storage.py,sha256=8K73KGLAVgchJdtd4rKHXkr1CQOJORWH-Gi57w8IYsw,21081
|
18
18
|
qulab/executor/template.py,sha256=dKQM3IlADdTi9qp0fnOYjyycRNEl7KeSCBZhwbmP8bQ,10828
|
19
19
|
qulab/executor/transform.py,sha256=rk4CLIKVjGRaFzi5FVSgadUxAKKVLSopEHZCaAzDwDg,3435
|
20
|
-
qulab/executor/utils.py,sha256=
|
20
|
+
qulab/executor/utils.py,sha256=3OLRMBJu-1t78BeuZs4fv4jioEXnRNygaPnSoibzfgs,6405
|
21
21
|
qulab/monitor/__init__.py,sha256=xEVDkJF8issrsDeLqQmDsvtRmrf-UiViFcGTWuzdlFU,43
|
22
22
|
qulab/monitor/__main__.py,sha256=k2H1H5Zf9LLXTDLISJkbikLH-z0f1e5i5i6wXXYPOrE,105
|
23
23
|
qulab/monitor/config.py,sha256=y_5StMkdrbZO1ziyKBrvIkB7Jclp9RCPK1QbsOhCxnY,785
|
@@ -62,10 +62,10 @@ qulab/sys/chat.py,sha256=t7wSlREOQ_Ke0pO9bfpkik9wY1qeiTbRF1dial9IbII,23137
|
|
62
62
|
qulab/sys/ipy_events.py,sha256=rGjsApv58_Gwd_TH6ecPnyXK34_ZYZsaX8tk32ozLGY,3014
|
63
63
|
qulab/sys/progress.py,sha256=2iYskzRdOC2EM4wtbzm0u1kovjGrF_A0W1kCxbftyzE,5540
|
64
64
|
qulab/sys/device/__init__.py,sha256=ZZxPJ9_MHModvghQoZOSWjIdeo3vhg2TJETDdrwQvkU,152
|
65
|
-
qulab/sys/device/basedevice.py,sha256=
|
65
|
+
qulab/sys/device/basedevice.py,sha256=mdKUzu0TpHIVMVQ4y7eADtlvGjFAjO-_NSEucLyVv3o,7450
|
66
66
|
qulab/sys/device/loader.py,sha256=RyUoykxGyfn-k22NWo-B06_AK_TMH8to4FzSkODLJHM,2583
|
67
67
|
qulab/sys/device/utils.py,sha256=5uqGOcaZlubCIw7gsknpB-tiFQyq8y6ebQxHcouQGUs,2393
|
68
|
-
qulab/sys/drivers/FakeInstrument.py,sha256=
|
68
|
+
qulab/sys/drivers/FakeInstrument.py,sha256=w5ItiveUppBoH2egZtfS6u7_heORA98iUdYeMjQkx8A,2474
|
69
69
|
qulab/sys/drivers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
70
70
|
qulab/sys/net/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
71
71
|
qulab/sys/net/bencoder.py,sha256=tsMjTG-E_EQokfWrZgJvxO3dTGaM6NOVzPPKsify23A,5164
|
@@ -99,9 +99,9 @@ qulab/visualization/plot_seq.py,sha256=Uo1-dB1YE9IN_A9tuaOs9ZG3S5dKDQ_l98iD2Wbxp
|
|
99
99
|
qulab/visualization/qdat.py,sha256=HubXFu4nfcA7iUzghJGle1C86G6221hicLR0b-GqhKQ,5887
|
100
100
|
qulab/visualization/rot3d.py,sha256=jGHJcqj1lEWBUV-W4GUGONGacqjrYvuFoFCwPse5h1Y,757
|
101
101
|
qulab/visualization/widgets.py,sha256=HcYwdhDtLreJiYaZuN3LfofjJmZcLwjMfP5aasebgDo,3266
|
102
|
-
qulab-2.10.
|
103
|
-
qulab-2.10.
|
104
|
-
qulab-2.10.
|
105
|
-
qulab-2.10.
|
106
|
-
qulab-2.10.
|
107
|
-
qulab-2.10.
|
102
|
+
qulab-2.10.2.dist-info/licenses/LICENSE,sha256=b4NRQ-GFVpJMT7RuExW3NwhfbrYsX7AcdB7Gudok-fs,1086
|
103
|
+
qulab-2.10.2.dist-info/METADATA,sha256=BbVR3fNk1YWnGToqB6gx0PpS4eaKZU9ht9en-2R60ws,3860
|
104
|
+
qulab-2.10.2.dist-info/WHEEL,sha256=_ZWIY2n7n6SpiuIFl1-RvcMp4Ty36T57FKf-7NzqZHM,101
|
105
|
+
qulab-2.10.2.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
|
106
|
+
qulab-2.10.2.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
|
107
|
+
qulab-2.10.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|