QuLab 2.10.10__cp313-cp313-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.
Files changed (107) hide show
  1. qulab/__init__.py +33 -0
  2. qulab/__main__.py +4 -0
  3. qulab/cli/__init__.py +0 -0
  4. qulab/cli/commands.py +30 -0
  5. qulab/cli/config.py +170 -0
  6. qulab/cli/decorators.py +28 -0
  7. qulab/dicttree.py +523 -0
  8. qulab/executor/__init__.py +5 -0
  9. qulab/executor/analyze.py +188 -0
  10. qulab/executor/cli.py +434 -0
  11. qulab/executor/load.py +563 -0
  12. qulab/executor/registry.py +185 -0
  13. qulab/executor/schedule.py +543 -0
  14. qulab/executor/storage.py +615 -0
  15. qulab/executor/template.py +259 -0
  16. qulab/executor/utils.py +194 -0
  17. qulab/expression.py +827 -0
  18. qulab/fun.cp313-win_amd64.pyd +0 -0
  19. qulab/monitor/__init__.py +1 -0
  20. qulab/monitor/__main__.py +8 -0
  21. qulab/monitor/config.py +41 -0
  22. qulab/monitor/dataset.py +77 -0
  23. qulab/monitor/event_queue.py +54 -0
  24. qulab/monitor/mainwindow.py +234 -0
  25. qulab/monitor/monitor.py +115 -0
  26. qulab/monitor/ploter.py +123 -0
  27. qulab/monitor/qt_compat.py +16 -0
  28. qulab/monitor/toolbar.py +265 -0
  29. qulab/scan/__init__.py +2 -0
  30. qulab/scan/curd.py +221 -0
  31. qulab/scan/models.py +554 -0
  32. qulab/scan/optimize.py +76 -0
  33. qulab/scan/query.py +387 -0
  34. qulab/scan/record.py +603 -0
  35. qulab/scan/scan.py +1166 -0
  36. qulab/scan/server.py +450 -0
  37. qulab/scan/space.py +213 -0
  38. qulab/scan/utils.py +234 -0
  39. qulab/storage/__init__.py +0 -0
  40. qulab/storage/__main__.py +51 -0
  41. qulab/storage/backend/__init__.py +0 -0
  42. qulab/storage/backend/redis.py +204 -0
  43. qulab/storage/base_dataset.py +352 -0
  44. qulab/storage/chunk.py +60 -0
  45. qulab/storage/dataset.py +127 -0
  46. qulab/storage/file.py +273 -0
  47. qulab/storage/models/__init__.py +22 -0
  48. qulab/storage/models/base.py +4 -0
  49. qulab/storage/models/config.py +28 -0
  50. qulab/storage/models/file.py +89 -0
  51. qulab/storage/models/ipy.py +58 -0
  52. qulab/storage/models/models.py +88 -0
  53. qulab/storage/models/record.py +161 -0
  54. qulab/storage/models/report.py +22 -0
  55. qulab/storage/models/tag.py +93 -0
  56. qulab/storage/storage.py +95 -0
  57. qulab/sys/__init__.py +2 -0
  58. qulab/sys/chat.py +688 -0
  59. qulab/sys/device/__init__.py +3 -0
  60. qulab/sys/device/basedevice.py +255 -0
  61. qulab/sys/device/loader.py +86 -0
  62. qulab/sys/device/utils.py +79 -0
  63. qulab/sys/drivers/FakeInstrument.py +68 -0
  64. qulab/sys/drivers/__init__.py +0 -0
  65. qulab/sys/ipy_events.py +125 -0
  66. qulab/sys/net/__init__.py +0 -0
  67. qulab/sys/net/bencoder.py +205 -0
  68. qulab/sys/net/cli.py +169 -0
  69. qulab/sys/net/dhcp.py +543 -0
  70. qulab/sys/net/dhcpd.py +176 -0
  71. qulab/sys/net/kad.py +1142 -0
  72. qulab/sys/net/kcp.py +192 -0
  73. qulab/sys/net/nginx.py +194 -0
  74. qulab/sys/progress.py +190 -0
  75. qulab/sys/rpc/__init__.py +0 -0
  76. qulab/sys/rpc/client.py +0 -0
  77. qulab/sys/rpc/exceptions.py +96 -0
  78. qulab/sys/rpc/msgpack.py +1052 -0
  79. qulab/sys/rpc/msgpack.pyi +41 -0
  80. qulab/sys/rpc/router.py +35 -0
  81. qulab/sys/rpc/rpc.py +412 -0
  82. qulab/sys/rpc/serialize.py +139 -0
  83. qulab/sys/rpc/server.py +29 -0
  84. qulab/sys/rpc/socket.py +29 -0
  85. qulab/sys/rpc/utils.py +25 -0
  86. qulab/sys/rpc/worker.py +0 -0
  87. qulab/sys/rpc/zmq_socket.py +227 -0
  88. qulab/tools/__init__.py +0 -0
  89. qulab/tools/connection_helper.py +39 -0
  90. qulab/typing.py +2 -0
  91. qulab/utils.py +95 -0
  92. qulab/version.py +1 -0
  93. qulab/visualization/__init__.py +188 -0
  94. qulab/visualization/__main__.py +71 -0
  95. qulab/visualization/_autoplot.py +464 -0
  96. qulab/visualization/plot_circ.py +319 -0
  97. qulab/visualization/plot_layout.py +408 -0
  98. qulab/visualization/plot_seq.py +242 -0
  99. qulab/visualization/qdat.py +152 -0
  100. qulab/visualization/rot3d.py +23 -0
  101. qulab/visualization/widgets.py +86 -0
  102. qulab-2.10.10.dist-info/METADATA +110 -0
  103. qulab-2.10.10.dist-info/RECORD +107 -0
  104. qulab-2.10.10.dist-info/WHEEL +5 -0
  105. qulab-2.10.10.dist-info/entry_points.txt +2 -0
  106. qulab-2.10.10.dist-info/licenses/LICENSE +21 -0
  107. qulab-2.10.10.dist-info/top_level.txt +1 -0
@@ -0,0 +1,543 @@
1
+ import functools
2
+ import inspect
3
+ import pickle
4
+ import uuid
5
+ from datetime import datetime, timedelta
6
+ from pathlib import Path
7
+
8
+ from loguru import logger
9
+
10
+ from .load import WorkflowType, get_dependents
11
+ from .registry import current_config, obey_the_oracle, update_parameters
12
+ from .storage import (Report, find_report, get_head, get_heads, renew_report,
13
+ revoke_report, save_item, save_report)
14
+
15
+ __session_id = None
16
+ __session_cache = {}
17
+
18
+
19
+ def set_cache(session_id, key, report: Report):
20
+ global __session_id
21
+ if __session_id is None:
22
+ __session_id = session_id
23
+ if __session_id != session_id:
24
+ __session_cache.clear()
25
+ if report.workflow.startswith('cfg:'):
26
+ __session_cache[key] = report
27
+ else:
28
+ __session_cache[key] = report.base_path, report.path
29
+
30
+
31
+ def get_cache(session_id, key) -> Report:
32
+ from .storage import load_report
33
+ global __session_id
34
+ if __session_id is None or __session_id != session_id:
35
+ return None
36
+ index = __session_cache.get(key, None)
37
+ if index is None:
38
+ return None
39
+ if isinstance(index, tuple):
40
+ base_path, path = index
41
+ return load_report(path, base_path)
42
+ elif isinstance(index, Report):
43
+ return index
44
+ else:
45
+ return None
46
+
47
+
48
+ class CalibrationFailedError(Exception):
49
+ pass
50
+
51
+
52
+ def is_pickleable(obj) -> bool:
53
+ try:
54
+ pickle.dumps(obj)
55
+ return True
56
+ except:
57
+ return False
58
+
59
+
60
+ def veryfy_analyzed_report(report: Report, script: str, method: str):
61
+ if not isinstance(report, Report):
62
+ raise TypeError(f'"{script}" : "{method}" must return a Report object')
63
+ if not is_pickleable(report.parameters):
64
+ raise TypeError(
65
+ f'"{script}" : "{method}" return not pickleable data in .parameters'
66
+ )
67
+ if not is_pickleable(report.other_infomation):
68
+ raise TypeError(
69
+ f'"{script}" : "{method}" return not pickleable data in .other_infomation'
70
+ )
71
+
72
+
73
+ def check_state(workflow: WorkflowType, code_path: str | Path,
74
+ state_path: str | Path, veryfy_source_code: bool) -> bool:
75
+ """
76
+ check state should report a pass if and only if the following are satisfied:
77
+
78
+ 1. The cal has had check data or calibrate pass within the timeout period.
79
+ 2. The cal has not failed calibrate without resolution.
80
+ 3. No dependencies have been recalibrated since the last time check data or calibrate was run on this cal.
81
+ 4. All dependencies pass check state.
82
+ """
83
+ logger.debug(f'check_state: "{workflow.__workflow_id__}"')
84
+ report = find_report(workflow.__workflow_id__, state_path)
85
+ if not report:
86
+ logger.debug(
87
+ f'check_state failed: No history found for "{workflow.__workflow_id__}"'
88
+ )
89
+ return False
90
+ if hasattr(workflow, 'check_state') and callable(workflow.check_state):
91
+ logger.debug(
92
+ f'check_state: "{workflow.__workflow_id__}" has custom check_state method'
93
+ )
94
+ return workflow.check_state(report)
95
+ if datetime.fromtimestamp(workflow.__mtime__) > report.checked_time:
96
+ logger.debug(
97
+ f'check_state failed: "{workflow.__workflow_id__}" has been modified after last calibration'
98
+ )
99
+ return False
100
+ if workflow.__timeout__ is not None and datetime.now(
101
+ ) > report.checked_time + timedelta(seconds=workflow.__timeout__):
102
+ logger.debug(
103
+ f'check_state failed: "{workflow.__workflow_id__}" has expired')
104
+ return False
105
+ if not report.in_spec:
106
+ logger.debug(
107
+ f'check_state failed: "{workflow.__workflow_id__}" is out of spec')
108
+ return False
109
+ if report.bad_data:
110
+ logger.debug(
111
+ f'check_state failed: "{workflow.__workflow_id__}" has bad data')
112
+ return False
113
+ for n in get_dependents(workflow, code_path, veryfy_source_code):
114
+ r = find_report(n.__workflow_id__, state_path)
115
+ if r is None or r.checked_time > report.checked_time:
116
+ logger.debug(
117
+ f'check_state failed: "{workflow.__workflow_id__}" has outdated dependencies'
118
+ )
119
+ return False
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
+ logger.debug(
123
+ f'check_state failed: "{workflow.__workflow_id__}" has bad dependencies'
124
+ )
125
+ return False
126
+ return True
127
+
128
+
129
+ @logger.catch(reraise=False)
130
+ async def call_plot(node: WorkflowType, report: Report, check=False):
131
+ if hasattr(node, 'plot') and callable(node.plot):
132
+ if inspect.iscoroutinefunction(node.plot):
133
+ await node.plot(report, check=check)
134
+ else:
135
+ node.plot(report)
136
+
137
+
138
+ async def call_check(workflow: WorkflowType, session_id: str,
139
+ state_path: Path):
140
+ report = get_cache(session_id, (workflow.__workflow_id__, 'check'))
141
+ if report is not None:
142
+ logger.debug(f'Cache hit for "{workflow.__workflow_id__}:check"')
143
+ return report
144
+
145
+ if inspect.iscoroutinefunction(workflow.check):
146
+ data = await workflow.check()
147
+ else:
148
+ data = workflow.check()
149
+ if not is_pickleable(data):
150
+ raise TypeError(
151
+ f'"{workflow.__workflow_id__}" : "check" return not pickleable data'
152
+ )
153
+ report = Report(workflow=workflow.__workflow_id__,
154
+ data=data,
155
+ config_path=current_config(state_path),
156
+ base_path=state_path,
157
+ heads=get_heads(state_path),
158
+ previous_path=get_head(workflow.__workflow_id__,
159
+ state_path),
160
+ script_path=save_item(workflow.__source__, 'items',
161
+ state_path))
162
+
163
+ save_report(workflow.__workflow_id__, report, state_path)
164
+
165
+ set_cache(session_id, (workflow.__workflow_id__, 'check'), report)
166
+ return report
167
+
168
+
169
+ async def call_calibrate(workflow: WorkflowType, session_id: str,
170
+ state_path: Path):
171
+ report = get_cache(session_id, (workflow.__workflow_id__, 'calibrate'))
172
+ if report is not None:
173
+ logger.debug(f'Cache hit for "{workflow.__workflow_id__}:calibrate"')
174
+ return report
175
+
176
+ if inspect.iscoroutinefunction(workflow.calibrate):
177
+ data = await workflow.calibrate()
178
+ else:
179
+ data = workflow.calibrate()
180
+ if not is_pickleable(data):
181
+ raise TypeError(
182
+ f'"{workflow.__workflow_id__}" : "calibrate" return not pickleable data'
183
+ )
184
+ report = Report(workflow=workflow.__workflow_id__,
185
+ data=data,
186
+ config_path=current_config(state_path),
187
+ base_path=state_path,
188
+ heads=get_heads(state_path),
189
+ previous_path=get_head(workflow.__workflow_id__,
190
+ state_path),
191
+ script_path=save_item(workflow.__source__, 'items',
192
+ state_path))
193
+
194
+ save_report(workflow.__workflow_id__, report, state_path)
195
+
196
+ set_cache(session_id, (workflow.__workflow_id__, 'calibrate'), report)
197
+ return report
198
+
199
+
200
+ async def call_check_analyzer(node: WorkflowType,
201
+ report: Report,
202
+ history: Report | None,
203
+ state_path: Path,
204
+ plot=False) -> Report:
205
+ if inspect.iscoroutinefunction(node.check_analyze):
206
+ report = await node.check_analyze(report, history=history)
207
+ else:
208
+ report = node.check_analyze(report, history=history)
209
+ veryfy_analyzed_report(report, node.__workflow_id__, "check_analyze")
210
+ report.fully_calibrated = False
211
+ if report.in_spec:
212
+ logger.debug(
213
+ f'"{node.__workflow_id__}": checked in spec, renewing report')
214
+ if report.previous is not None:
215
+ renew_report(node.__workflow_id__, report.previous, state_path)
216
+ else:
217
+ renew_report(node.__workflow_id__, report, state_path)
218
+ else:
219
+ logger.debug(
220
+ f'"{node.__workflow_id__}": checked out of spec, revoking report')
221
+ if report.previous is not None:
222
+ revoke_report(node.__workflow_id__, report.previous, state_path)
223
+ else:
224
+ revoke_report(node.__workflow_id__, report, state_path)
225
+ return report
226
+
227
+
228
+ async def call_analyzer(node: WorkflowType,
229
+ report: Report,
230
+ history: Report | None,
231
+ state_path: Path,
232
+ plot=False) -> Report:
233
+ if inspect.iscoroutinefunction(node.analyze):
234
+ report = await node.analyze(report, history=history)
235
+ else:
236
+ report = node.analyze(report, history=history)
237
+ veryfy_analyzed_report(report, node.__workflow_id__, "analyze")
238
+ if hasattr(node, 'oracle') and callable(node.oracle):
239
+ logger.debug(
240
+ f'"{node.__workflow_id__}" has oracle method, calling ...')
241
+ report = await call_oracle(node, report, history)
242
+ report.fully_calibrated = True
243
+ save_report(node.__workflow_id__, report, state_path, overwrite=True)
244
+ if plot:
245
+ await call_plot(node, report)
246
+ return report
247
+
248
+
249
+ async def call_oracle(node: WorkflowType, report: Report,
250
+ history: Report | None):
251
+ sig = inspect.signature(node.oracle)
252
+ try:
253
+ if 'history' in sig.parameters and 'system_state' in sig.parameters:
254
+ report = node.oracle(report,
255
+ history=history,
256
+ system_state=get_heads(report.base_path))
257
+ elif 'history' in sig.parameters:
258
+ report = node.oracle(report, history=history)
259
+ elif 'system_state' in sig.parameters:
260
+ report = node.oracle(report,
261
+ system_state=get_heads(report.base_path))
262
+ else:
263
+ report = node.oracle(report)
264
+ if inspect.isawaitable(report):
265
+ report = await report
266
+ except Exception as e:
267
+ logger.exception(e)
268
+ report.oracle = {}
269
+ return report
270
+ if not isinstance(report, Report):
271
+ raise TypeError(
272
+ f'"{node.__workflow_id__}" : function "oracle" must return a Report object'
273
+ )
274
+ if not is_pickleable(report.oracle):
275
+ raise TypeError(
276
+ f'"{node.__workflow_id__}" : function "oracle" return not pickleable data'
277
+ )
278
+ return report
279
+
280
+
281
+ async def check_data(workflow: WorkflowType, state_path: str | Path,
282
+ plot: bool, session_id: str) -> Report:
283
+ """
284
+ check data answers two questions:
285
+ Is the parameter associated with this cal in spec,
286
+ and is the cal scan working as expected?
287
+ """
288
+ history = find_report(workflow.__workflow_id__, state_path)
289
+
290
+ if history is None:
291
+ logger.debug(f'No history found for "{workflow.__workflow_id__}"')
292
+ report = Report(workflow=workflow.__workflow_id__,
293
+ config_path=current_config(state_path),
294
+ base_path=state_path,
295
+ heads=get_heads(state_path),
296
+ previous_path=get_head(workflow.__workflow_id__,
297
+ state_path),
298
+ script_path=save_item(workflow.__source__, 'items',
299
+ state_path))
300
+ report.in_spec = False
301
+ report.bad_data = False
302
+ return report
303
+
304
+ if history.bad_data:
305
+ logger.debug(
306
+ f'History found for "{workflow.__workflow_id__}", but bad data')
307
+ return history
308
+ if not history.in_spec:
309
+ logger.debug(
310
+ f'History found for "{workflow.__workflow_id__}", but out of spec')
311
+ return history
312
+
313
+ logger.debug(
314
+ f'History found for "{workflow.__workflow_id__}", but has expired')
315
+
316
+ if hasattr(workflow, 'check') and callable(workflow.check) and hasattr(
317
+ workflow, 'check_analyze') and callable(workflow.check_analyze):
318
+ logger.debug(
319
+ f'Checking "{workflow.__workflow_id__}" with "check" method ...')
320
+
321
+ report = await call_check(workflow, session_id, state_path)
322
+
323
+ logger.debug(f'Checked "{workflow.__workflow_id__}" !')
324
+ report = await call_check_analyzer(workflow,
325
+ report,
326
+ history,
327
+ state_path,
328
+ plot=plot)
329
+ else:
330
+ logger.debug(
331
+ f'Checking "{workflow.__workflow_id__}" with "calibrate" method ...'
332
+ )
333
+
334
+ report = await call_calibrate(workflow, session_id, state_path)
335
+
336
+ logger.debug(f'Calibrated "{workflow.__workflow_id__}" !')
337
+ report = await call_analyzer(workflow,
338
+ report,
339
+ history,
340
+ state_path,
341
+ plot=plot)
342
+ return report
343
+
344
+
345
+ async def calibrate(workflow: WorkflowType, state_path: str | Path, plot: bool,
346
+ session_id: str) -> Report:
347
+ history = find_report(workflow.__workflow_id__, state_path)
348
+
349
+ logger.debug(f'Calibrating "{workflow.__workflow_id__}" ...')
350
+
351
+ report = await call_calibrate(workflow, session_id, state_path)
352
+
353
+ logger.debug(f'Calibrated "{workflow.__workflow_id__}" !')
354
+
355
+ report = await call_analyzer(workflow,
356
+ report,
357
+ history,
358
+ state_path,
359
+ plot=plot)
360
+ return report
361
+
362
+
363
+ async def diagnose(workflow: WorkflowType, code_path: str | Path,
364
+ state_path: str | Path, plot: bool, session_id: str,
365
+ fail_fast: bool, veryfy_source_code: bool):
366
+ '''
367
+ Returns: True if node or dependent recalibrated.
368
+ '''
369
+ logger.debug(f'diagnose "{workflow.__workflow_id__}"')
370
+ # check_data
371
+ report = await check_data(workflow, state_path, plot, session_id)
372
+ # in spec case
373
+ if report.in_spec:
374
+ logger.debug(
375
+ f'"{workflow.__workflow_id__}": Checked! In spec, no need to diagnose'
376
+ )
377
+ return False
378
+ # bad data case
379
+ recalibrated = []
380
+ if report.bad_data:
381
+ logger.debug(
382
+ f'"{workflow.__workflow_id__}": Bad data, diagnosing dependents')
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, fail_fast,
389
+ veryfy_source_code)
390
+ except Exception as e:
391
+ if fail_fast:
392
+ raise e
393
+ exceptions.append(e)
394
+ recalibrated.append(flag)
395
+ if any(exceptions):
396
+ raise exceptions[0]
397
+ if not any(recalibrated):
398
+ if report.bad_data:
399
+ raise CalibrationFailedError(
400
+ f'"{workflow.__workflow_id__}": bad data but no dependents recalibrated.'
401
+ )
402
+ logger.debug(
403
+ f'"{workflow.__workflow_id__}": no dependents recalibrated.')
404
+ # calibrate
405
+ if any(recalibrated):
406
+ logger.debug(
407
+ f'recalibrate "{workflow.__workflow_id__}" because some dependents recalibrated.'
408
+ )
409
+ elif not report.in_spec and not report.bad_data:
410
+ logger.debug(
411
+ f'recalibrate "{workflow.__workflow_id__}" because out of spec.')
412
+ elif report.in_spec:
413
+ logger.error(
414
+ f'Never reach: recalibrate "{workflow.__workflow_id__}" because in spec.'
415
+ )
416
+ elif report.bad_data:
417
+ logger.error(
418
+ f'Never reach: recalibrate "{workflow.__workflow_id__}" because bad data.'
419
+ )
420
+ else:
421
+ logger.error(f'Never reach: recalibrate "{workflow.__workflow_id__}"')
422
+
423
+ report = await calibrate(workflow, state_path, plot, session_id)
424
+ if report.bad_data or not report.in_spec:
425
+ obey_the_oracle(report, state_path)
426
+ raise CalibrationFailedError(
427
+ f'"{workflow.__workflow_id__}": All dependents passed, but calibration failed!'
428
+ )
429
+ update_parameters(report, state_path)
430
+ logger.debug(f'"{workflow.__workflow_id__}": parameters updated')
431
+ return True
432
+
433
+
434
+ @logger.catch(reraise=True)
435
+ async def maintain(workflow: WorkflowType,
436
+ code_path: str | Path,
437
+ state_path: str | Path,
438
+ session_id: str | None = None,
439
+ run: bool = False,
440
+ plot: bool = False,
441
+ freeze: bool = False,
442
+ fail_fast: bool = False,
443
+ veryfy_source_code: bool = True):
444
+ if session_id is None:
445
+ session_id = uuid.uuid4().hex
446
+ logger.debug(f'run "{workflow.__workflow_id__}"'
447
+ if run else f'maintain "{workflow.__workflow_id__}"')
448
+ # recursive maintain
449
+ exceptions = []
450
+ for n in get_dependents(workflow, code_path, veryfy_source_code):
451
+ logger.debug(
452
+ f'maintain "{n.__workflow_id__}" because it is depended by "{workflow.__workflow_id__}"'
453
+ )
454
+ try:
455
+ await maintain(n,
456
+ code_path,
457
+ state_path,
458
+ session_id,
459
+ run=False,
460
+ plot=plot,
461
+ freeze=freeze,
462
+ fail_fast=fail_fast,
463
+ veryfy_source_code=veryfy_source_code)
464
+ except Exception as e:
465
+ if fail_fast:
466
+ raise e
467
+ exceptions.append(e)
468
+ else:
469
+ logger.debug(
470
+ f'"{workflow.__workflow_id__}": All dependents maintained')
471
+ if any(exceptions):
472
+ raise exceptions[0]
473
+ # check_state
474
+ if check_state(workflow, code_path, state_path,
475
+ veryfy_source_code) and not run:
476
+ logger.debug(
477
+ f'"{workflow.__workflow_id__}": In spec, no need to maintain')
478
+ return
479
+ # check_data
480
+ report = await check_data(workflow, state_path, plot, session_id)
481
+ if report.in_spec:
482
+ if not run:
483
+ logger.debug(
484
+ f'"{workflow.__workflow_id__}": In spec, no need to maintain')
485
+ return
486
+ elif report.bad_data:
487
+ logger.debug(
488
+ f'"{workflow.__workflow_id__}": Bad data, diagnosing dependents')
489
+ exceptions = []
490
+ for n in get_dependents(workflow, code_path, veryfy_source_code):
491
+ logger.debug(
492
+ f'diagnose "{n.__workflow_id__}" because of "{workflow.__workflow_id__}" bad data'
493
+ )
494
+ try:
495
+ await diagnose(n, code_path, state_path, plot, session_id,
496
+ fail_fast, veryfy_source_code)
497
+ except Exception as e:
498
+ if fail_fast:
499
+ raise e
500
+ exceptions.append(e)
501
+ else:
502
+ logger.debug(
503
+ f'"{workflow.__workflow_id__}": All dependents diagnosed')
504
+ if any(exceptions):
505
+ raise exceptions[0]
506
+ # calibrate
507
+ logger.debug(f'recalibrate "{workflow.__workflow_id__}"')
508
+ report = await calibrate(workflow, state_path, plot, session_id)
509
+ if report.bad_data or not report.in_spec:
510
+ if not freeze:
511
+ obey_the_oracle(report, state_path)
512
+ raise CalibrationFailedError(
513
+ f'"{workflow.__workflow_id__}": All dependents passed, but calibration failed!'
514
+ )
515
+ if not freeze:
516
+ update_parameters(report, state_path)
517
+ logger.debug(f'"{workflow.__workflow_id__}": parameters updated')
518
+ else:
519
+ logger.debug(f'"{workflow.__workflow_id__}": parameters freezed')
520
+ return
521
+
522
+
523
+ @logger.catch(reraise=True)
524
+ async def run(workflow: WorkflowType,
525
+ code_path: str | Path,
526
+ state_path: str | Path,
527
+ plot: bool = False,
528
+ freeze: bool = False):
529
+ session_id = uuid.uuid4().hex
530
+ logger.debug(f'run "{workflow.__workflow_id__}" without dependences.')
531
+ report = await calibrate(workflow, state_path, plot, session_id=session_id)
532
+ if report.bad_data or not report.in_spec:
533
+ if not freeze:
534
+ obey_the_oracle(report, state_path)
535
+ raise CalibrationFailedError(
536
+ f'"{workflow.__workflow_id__}": All dependents passed, but calibration failed!'
537
+ )
538
+ if not freeze:
539
+ update_parameters(report, state_path)
540
+ logger.debug(f'"{workflow.__workflow_id__}": parameters updated')
541
+ else:
542
+ logger.debug(f'"{workflow.__workflow_id__}": parameters freezed')
543
+ return