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.
- qulab/__init__.py +33 -0
- qulab/__main__.py +4 -0
- qulab/cli/__init__.py +0 -0
- qulab/cli/commands.py +30 -0
- qulab/cli/config.py +170 -0
- qulab/cli/decorators.py +28 -0
- qulab/dicttree.py +523 -0
- qulab/executor/__init__.py +5 -0
- qulab/executor/analyze.py +188 -0
- qulab/executor/cli.py +434 -0
- qulab/executor/load.py +563 -0
- qulab/executor/registry.py +185 -0
- qulab/executor/schedule.py +543 -0
- qulab/executor/storage.py +615 -0
- qulab/executor/template.py +259 -0
- qulab/executor/utils.py +194 -0
- qulab/expression.py +827 -0
- qulab/fun.cp313-win_amd64.pyd +0 -0
- qulab/monitor/__init__.py +1 -0
- qulab/monitor/__main__.py +8 -0
- qulab/monitor/config.py +41 -0
- qulab/monitor/dataset.py +77 -0
- qulab/monitor/event_queue.py +54 -0
- qulab/monitor/mainwindow.py +234 -0
- qulab/monitor/monitor.py +115 -0
- qulab/monitor/ploter.py +123 -0
- qulab/monitor/qt_compat.py +16 -0
- qulab/monitor/toolbar.py +265 -0
- qulab/scan/__init__.py +2 -0
- qulab/scan/curd.py +221 -0
- qulab/scan/models.py +554 -0
- qulab/scan/optimize.py +76 -0
- qulab/scan/query.py +387 -0
- qulab/scan/record.py +603 -0
- qulab/scan/scan.py +1166 -0
- qulab/scan/server.py +450 -0
- qulab/scan/space.py +213 -0
- qulab/scan/utils.py +234 -0
- qulab/storage/__init__.py +0 -0
- qulab/storage/__main__.py +51 -0
- qulab/storage/backend/__init__.py +0 -0
- qulab/storage/backend/redis.py +204 -0
- qulab/storage/base_dataset.py +352 -0
- qulab/storage/chunk.py +60 -0
- qulab/storage/dataset.py +127 -0
- qulab/storage/file.py +273 -0
- qulab/storage/models/__init__.py +22 -0
- qulab/storage/models/base.py +4 -0
- qulab/storage/models/config.py +28 -0
- qulab/storage/models/file.py +89 -0
- qulab/storage/models/ipy.py +58 -0
- qulab/storage/models/models.py +88 -0
- qulab/storage/models/record.py +161 -0
- qulab/storage/models/report.py +22 -0
- qulab/storage/models/tag.py +93 -0
- qulab/storage/storage.py +95 -0
- qulab/sys/__init__.py +2 -0
- qulab/sys/chat.py +688 -0
- qulab/sys/device/__init__.py +3 -0
- qulab/sys/device/basedevice.py +255 -0
- qulab/sys/device/loader.py +86 -0
- qulab/sys/device/utils.py +79 -0
- qulab/sys/drivers/FakeInstrument.py +68 -0
- qulab/sys/drivers/__init__.py +0 -0
- qulab/sys/ipy_events.py +125 -0
- qulab/sys/net/__init__.py +0 -0
- qulab/sys/net/bencoder.py +205 -0
- qulab/sys/net/cli.py +169 -0
- qulab/sys/net/dhcp.py +543 -0
- qulab/sys/net/dhcpd.py +176 -0
- qulab/sys/net/kad.py +1142 -0
- qulab/sys/net/kcp.py +192 -0
- qulab/sys/net/nginx.py +194 -0
- qulab/sys/progress.py +190 -0
- qulab/sys/rpc/__init__.py +0 -0
- qulab/sys/rpc/client.py +0 -0
- qulab/sys/rpc/exceptions.py +96 -0
- qulab/sys/rpc/msgpack.py +1052 -0
- qulab/sys/rpc/msgpack.pyi +41 -0
- qulab/sys/rpc/router.py +35 -0
- qulab/sys/rpc/rpc.py +412 -0
- qulab/sys/rpc/serialize.py +139 -0
- qulab/sys/rpc/server.py +29 -0
- qulab/sys/rpc/socket.py +29 -0
- qulab/sys/rpc/utils.py +25 -0
- qulab/sys/rpc/worker.py +0 -0
- qulab/sys/rpc/zmq_socket.py +227 -0
- qulab/tools/__init__.py +0 -0
- qulab/tools/connection_helper.py +39 -0
- qulab/typing.py +2 -0
- qulab/utils.py +95 -0
- qulab/version.py +1 -0
- qulab/visualization/__init__.py +188 -0
- qulab/visualization/__main__.py +71 -0
- qulab/visualization/_autoplot.py +464 -0
- qulab/visualization/plot_circ.py +319 -0
- qulab/visualization/plot_layout.py +408 -0
- qulab/visualization/plot_seq.py +242 -0
- qulab/visualization/qdat.py +152 -0
- qulab/visualization/rot3d.py +23 -0
- qulab/visualization/widgets.py +86 -0
- qulab-2.10.10.dist-info/METADATA +110 -0
- qulab-2.10.10.dist-info/RECORD +107 -0
- qulab-2.10.10.dist-info/WHEEL +5 -0
- qulab-2.10.10.dist-info/entry_points.txt +2 -0
- qulab-2.10.10.dist-info/licenses/LICENSE +21 -0
- 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
|