QuLab 2.3.3__cp310-cp310-macosx_10_9_universal2.whl → 2.3.5__cp310-cp310-macosx_10_9_universal2.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-2.3.3.dist-info → QuLab-2.3.5.dist-info}/METADATA +4 -1
- {QuLab-2.3.3.dist-info → QuLab-2.3.5.dist-info}/RECORD +13 -13
- {QuLab-2.3.3.dist-info → QuLab-2.3.5.dist-info}/WHEEL +1 -1
- qulab/fun.cpython-310-darwin.so +0 -0
- qulab/scan/optimize.py +1 -1
- qulab/scan/scan.py +284 -282
- qulab/scan/server.py +69 -38
- qulab/scan/space.py +18 -3
- qulab/scan/utils.py +98 -0
- qulab/version.py +1 -1
- {QuLab-2.3.3.dist-info → QuLab-2.3.5.dist-info}/LICENSE +0 -0
- {QuLab-2.3.3.dist-info → QuLab-2.3.5.dist-info}/entry_points.txt +0 -0
- {QuLab-2.3.3.dist-info → QuLab-2.3.5.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: QuLab
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.5
|
|
4
4
|
Summary: contral instruments and manage data
|
|
5
5
|
Author-email: feihoo87 <feihoo87@gmail.com>
|
|
6
6
|
Maintainer-email: feihoo87 <feihoo87@gmail.com>
|
|
@@ -38,6 +38,9 @@ Requires-Dist: ply >=3.11
|
|
|
38
38
|
Requires-Dist: pyzmq >=25.1.0
|
|
39
39
|
Requires-Dist: scipy >=1.0.0
|
|
40
40
|
Requires-Dist: watchdog >=4.0.0
|
|
41
|
+
Provides-Extra: full
|
|
42
|
+
Requires-Dist: SQLAlchemy >=2.0.19 ; extra == 'full'
|
|
43
|
+
Requires-Dist: uvloop >=0.19.0 ; extra == 'full'
|
|
41
44
|
|
|
42
45
|
# QuLab
|
|
43
46
|
[](https://travis-ci.org/feihoo87/QuLab)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
qulab/__init__.py,sha256=P-Mx2p4TVmL91SoxoeXcj8Qm0x4xUf5Q_FLk0Yc_gIQ,138
|
|
2
2
|
qulab/__main__.py,sha256=ZC1NKaoxKyy60DaCfB8vYnB1z3RXQ2j8E1sRZ4A8sXE,428
|
|
3
|
-
qulab/fun.cpython-310-darwin.so,sha256=
|
|
4
|
-
qulab/version.py,sha256=
|
|
3
|
+
qulab/fun.cpython-310-darwin.so,sha256=Unem2rqcgqTtFG33bel_diRj9DMVgXAks4mDn1ZLh28,159632
|
|
4
|
+
qulab/version.py,sha256=ctNy09QkNipfMu2fxyFBsAXtRoAHMP1bEKHsc3zRmkk,21
|
|
5
5
|
qulab/monitor/__init__.py,sha256=nTHelnDpxRS_fl_B38TsN0njgq8eVTEz9IAnN3NbDlM,42
|
|
6
6
|
qulab/monitor/__main__.py,sha256=w3yUcqq195LzSnXTkQcuC1RSFRhy4oQ_PEBmucXguME,97
|
|
7
7
|
qulab/monitor/config.py,sha256=fQ5JcsMApKc1UwANEnIvbDQZl8uYW0tle92SaYtX9lI,744
|
|
@@ -16,13 +16,13 @@ qulab/scan/__init__.py,sha256=ZX4WsvqYxvJeHLgGSrtJoAnVU94gxY7EHKMxYooMERg,130
|
|
|
16
16
|
qulab/scan/curd.py,sha256=thq_qfi3qng3Zx-1uhNG64IQhGCuum_LR4MOKnS8cDI,6896
|
|
17
17
|
qulab/scan/expression.py,sha256=ngWrP1o9CuYJ1gq5YHaV7EfxKIKUX7Gz6KG80E6XThY,20070
|
|
18
18
|
qulab/scan/models.py,sha256=5Jpo25WGMWs0GtLzYLsWO61G3-FFYx5BHhBr2b6rOTE,17681
|
|
19
|
-
qulab/scan/optimize.py,sha256=
|
|
19
|
+
qulab/scan/optimize.py,sha256=VT9TpuqDkG7FdJJkYy60r5Pfrmjvfu5i36Ru6XvIiTI,2561
|
|
20
20
|
qulab/scan/query.py,sha256=-5uHMhXSyGovK1oy_uUbGVEbRFzaMBkP78ZMNfI3jD0,11809
|
|
21
21
|
qulab/scan/record.py,sha256=yIHPANf6nuBXy8Igf-dMtGJ7wuFTLYlBaaAUc0AzwyU,21280
|
|
22
|
-
qulab/scan/scan.py,sha256=
|
|
23
|
-
qulab/scan/server.py,sha256=
|
|
24
|
-
qulab/scan/space.py,sha256=
|
|
25
|
-
qulab/scan/utils.py,sha256=
|
|
22
|
+
qulab/scan/scan.py,sha256=iXvbnXLZvHa4v88MlYiZ_LYubEB7ZfbX7OiFTMhl_1o,39479
|
|
23
|
+
qulab/scan/server.py,sha256=Gh5DTacqkE4FD8BtynSuBdl7Ifwc52TMlGyXknVH4q8,19957
|
|
24
|
+
qulab/scan/space.py,sha256=OQLepiNNP5TNYMHXeVFT59lL5n4soPmnMoMy_o9EHt0,6696
|
|
25
|
+
qulab/scan/utils.py,sha256=SzJ_c4cLZJzERZr_CJO1_4juOxjfwCpU2K1mkc1PWGM,6124
|
|
26
26
|
qulab/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
27
|
qulab/storage/__main__.py,sha256=3emxxRry8BB0m8hUZvJ_oBqkPy7ksV7flHB_KEDXZuI,1692
|
|
28
28
|
qulab/storage/base_dataset.py,sha256=4aKhqBNdZfdlm_z1Qy5dv0HrvgpvMdy8pbyfua8uE-4,11865
|
|
@@ -78,9 +78,9 @@ qulab/visualization/plot_layout.py,sha256=clNw9QjE_kVNpIIx2Ob4YhAz2fucPGMuzkoIrO
|
|
|
78
78
|
qulab/visualization/plot_seq.py,sha256=lphYF4VhkEdc_wWr1kFBwrx2yujkyFPFaJ3pjr61awI,2693
|
|
79
79
|
qulab/visualization/qdat.py,sha256=ZeevBYWkzbww4xZnsjHhw7wRorJCBzbG0iEu-XQB4EA,5735
|
|
80
80
|
qulab/visualization/widgets.py,sha256=6KkiTyQ8J-ei70LbPQZAK35wjktY47w2IveOa682ftA,3180
|
|
81
|
-
QuLab-2.3.
|
|
82
|
-
QuLab-2.3.
|
|
83
|
-
QuLab-2.3.
|
|
84
|
-
QuLab-2.3.
|
|
85
|
-
QuLab-2.3.
|
|
86
|
-
QuLab-2.3.
|
|
81
|
+
QuLab-2.3.5.dist-info/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
|
|
82
|
+
QuLab-2.3.5.dist-info/METADATA,sha256=oI912BvgopGv3rP89guaGd6eXLIybHJZa6SrJdaD7As,3633
|
|
83
|
+
QuLab-2.3.5.dist-info/WHEEL,sha256=LLSyqktal3jZJhWNtlg7HaUJKl3AGKH2j3EL7wpWyD4,114
|
|
84
|
+
QuLab-2.3.5.dist-info/entry_points.txt,sha256=ohBzutEnQimP_BZWiuXdSliu4QAYSHHcN0PZD8c7ZCY,46
|
|
85
|
+
QuLab-2.3.5.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
|
|
86
|
+
QuLab-2.3.5.dist-info/RECORD,,
|
qulab/fun.cpython-310-darwin.so
CHANGED
|
Binary file
|
qulab/scan/optimize.py
CHANGED
qulab/scan/scan.py
CHANGED
|
@@ -6,9 +6,7 @@ import itertools
|
|
|
6
6
|
import lzma
|
|
7
7
|
import os
|
|
8
8
|
import pickle
|
|
9
|
-
import platform
|
|
10
9
|
import re
|
|
11
|
-
import subprocess
|
|
12
10
|
import sys
|
|
13
11
|
import uuid
|
|
14
12
|
from concurrent.futures import ProcessPoolExecutor
|
|
@@ -26,7 +24,8 @@ from .optimize import NgOptimizer
|
|
|
26
24
|
from .record import Record
|
|
27
25
|
from .server import default_record_port
|
|
28
26
|
from .space import Optimizer, OptimizeSpace, Space
|
|
29
|
-
from .utils import async_zip, call_function, dump_globals
|
|
27
|
+
from .utils import (async_zip, call_function, dump_dict, dump_globals,
|
|
28
|
+
get_installed_packages, get_system_info, yapf_reformat)
|
|
30
29
|
|
|
31
30
|
try:
|
|
32
31
|
from tqdm.notebook import tqdm
|
|
@@ -58,57 +57,34 @@ else:
|
|
|
58
57
|
default_executor = default_server
|
|
59
58
|
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
import isort
|
|
64
|
-
import yapf.yapflib.yapf_api
|
|
65
|
-
|
|
66
|
-
fname = f"f{uuid.uuid1().hex}"
|
|
67
|
-
|
|
68
|
-
def wrap(source):
|
|
69
|
-
lines = [f"async def {fname}():"]
|
|
70
|
-
for line in source.split('\n'):
|
|
71
|
-
lines.append(" " + line)
|
|
72
|
-
return '\n'.join(lines)
|
|
60
|
+
class Promise():
|
|
61
|
+
__slots__ = ['task', 'key', 'attr']
|
|
73
62
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
continue
|
|
79
|
-
lines.append(line[4:])
|
|
80
|
-
return '\n'.join(lines)
|
|
63
|
+
def __init__(self, task, key=None, attr=None):
|
|
64
|
+
self.task = task
|
|
65
|
+
self.key = key
|
|
66
|
+
self.attr = attr
|
|
81
67
|
|
|
82
|
-
|
|
83
|
-
reformated_text = unwrap(
|
|
84
|
-
yapf.yapflib.yapf_api.FormatCode(wrap(isort.code(cell_text)))[0])
|
|
85
|
-
return re.sub('^#%#', '%', reformated_text, flags=re.M)
|
|
86
|
-
except:
|
|
87
|
-
return cell_text
|
|
68
|
+
def __await__(self):
|
|
88
69
|
|
|
70
|
+
async def _getitem(task, key):
|
|
71
|
+
return (await task)[key]
|
|
89
72
|
|
|
90
|
-
def
|
|
91
|
-
|
|
92
|
-
stdout=subprocess.PIPE,
|
|
93
|
-
text=True)
|
|
73
|
+
async def _getattr(task, attr):
|
|
74
|
+
return getattr(await task, attr)
|
|
94
75
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
76
|
+
if self.key is not None:
|
|
77
|
+
return _getitem(self.task, self.key).__await__()
|
|
78
|
+
elif self.attr is not None:
|
|
79
|
+
return _getattr(self.task, self.attr).__await__()
|
|
80
|
+
else:
|
|
81
|
+
return self.task.__await__()
|
|
101
82
|
|
|
83
|
+
def __getitem__(self, key):
|
|
84
|
+
return Promise(self.task, key, None)
|
|
102
85
|
|
|
103
|
-
def
|
|
104
|
-
|
|
105
|
-
'OS': platform.uname()._asdict(),
|
|
106
|
-
'Python': sys.version,
|
|
107
|
-
'PythonExecutable': sys.executable,
|
|
108
|
-
'PythonPath': sys.path,
|
|
109
|
-
'packages': get_installed_packages()
|
|
110
|
-
}
|
|
111
|
-
return info
|
|
86
|
+
def __getattr__(self, attr):
|
|
87
|
+
return Promise(self.task, None, attr)
|
|
112
88
|
|
|
113
89
|
|
|
114
90
|
def current_notebook():
|
|
@@ -170,52 +146,185 @@ def _get_depends(func: Callable):
|
|
|
170
146
|
return args
|
|
171
147
|
|
|
172
148
|
|
|
173
|
-
|
|
174
|
-
|
|
149
|
+
def _run_function_in_process(buf):
|
|
150
|
+
func, args, kwds = dill.loads(buf)
|
|
151
|
+
return func(*args, **kwds)
|
|
175
152
|
|
|
176
|
-
def __init__(self, task, key=None, attr=None):
|
|
177
|
-
self.task = task
|
|
178
|
-
self.key = key
|
|
179
|
-
self.attr = attr
|
|
180
153
|
|
|
181
|
-
|
|
154
|
+
async def update_variables(variables: dict[str, Any], updates: dict[str, Any],
|
|
155
|
+
setters: dict[str, Callable]):
|
|
156
|
+
coros = []
|
|
157
|
+
for name, value in updates.items():
|
|
158
|
+
if name in setters:
|
|
159
|
+
coro = setters[name](value)
|
|
160
|
+
if inspect.isawaitable(coro):
|
|
161
|
+
coros.append(coro)
|
|
162
|
+
variables[name] = value
|
|
163
|
+
if coros:
|
|
164
|
+
await asyncio.gather(*coros)
|
|
182
165
|
|
|
183
|
-
async def _getitem(task, key):
|
|
184
|
-
return (await task)[key]
|
|
185
166
|
|
|
186
|
-
|
|
187
|
-
|
|
167
|
+
async def _iter_level(variables,
|
|
168
|
+
iters: list[tuple[str, Iterable | Expression | Callable
|
|
169
|
+
| OptimizeSpace]],
|
|
170
|
+
order: list[list[str]],
|
|
171
|
+
functions: dict[str, Callable | Expression],
|
|
172
|
+
optimizers: dict[str, Optimizer],
|
|
173
|
+
setters: dict[str, Callable] = {},
|
|
174
|
+
getters: dict[str, Callable] = {}):
|
|
175
|
+
iters_d = {}
|
|
176
|
+
env = Env()
|
|
177
|
+
env.variables = variables
|
|
178
|
+
opts = {}
|
|
188
179
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
180
|
+
for name, iter in iters:
|
|
181
|
+
if isinstance(iter, OptimizeSpace):
|
|
182
|
+
if iter.optimizer.name not in opts:
|
|
183
|
+
opts[iter.optimizer.name] = iter.optimizer.create()
|
|
184
|
+
elif isinstance(iter, Expression):
|
|
185
|
+
iters_d[name] = iter.eval(env)
|
|
186
|
+
elif isinstance(iter, Space):
|
|
187
|
+
iters_d[name] = iter.toarray()
|
|
188
|
+
elif callable(iter):
|
|
189
|
+
iters_d[name] = await call_function(iter, variables)
|
|
193
190
|
else:
|
|
194
|
-
|
|
191
|
+
iters_d[name] = iter
|
|
195
192
|
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
maxiter = 0xffffffff
|
|
194
|
+
for name, opt in opts.items():
|
|
195
|
+
opt_cfg = optimizers[name]
|
|
196
|
+
maxiter = min(maxiter, opt_cfg.maxiter)
|
|
198
197
|
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
async for args in async_zip(*iters_d.values(), range(maxiter)):
|
|
199
|
+
await update_variables(variables, dict(zip(iters_d.keys(), args[:-1])),
|
|
200
|
+
setters)
|
|
201
|
+
for name, opt in opts.items():
|
|
202
|
+
args = opt.ask()
|
|
203
|
+
opt_cfg = optimizers[name]
|
|
204
|
+
await update_variables(variables, {
|
|
205
|
+
n: v
|
|
206
|
+
for n, v in zip(opt_cfg.dimensions.keys(), args)
|
|
207
|
+
}, setters)
|
|
201
208
|
|
|
209
|
+
await update_variables(
|
|
210
|
+
variables, await call_many_functions(order, functions, variables),
|
|
211
|
+
setters)
|
|
202
212
|
|
|
203
|
-
|
|
204
|
-
func, args, kwds = dill.loads(buf)
|
|
205
|
-
return func(*args, **kwds)
|
|
213
|
+
yield variables
|
|
206
214
|
|
|
215
|
+
variables.update(await call_many_functions(order, getters, variables))
|
|
207
216
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
217
|
+
if opts:
|
|
218
|
+
for key in list(variables.keys()):
|
|
219
|
+
if key.startswith('*') or ',' in key:
|
|
220
|
+
await _unpack(key, variables)
|
|
221
|
+
|
|
222
|
+
for name, opt in opts.items():
|
|
223
|
+
opt_cfg = optimizers[name]
|
|
224
|
+
args = [variables[n] for n in opt_cfg.dimensions.keys()]
|
|
225
|
+
|
|
226
|
+
if name not in variables:
|
|
227
|
+
raise ValueError(f'{name} not in variables.')
|
|
228
|
+
fun = variables[name]
|
|
229
|
+
if inspect.isawaitable(fun):
|
|
230
|
+
fun = await fun
|
|
231
|
+
if opt_cfg.minimize:
|
|
232
|
+
opt.tell(args, fun)
|
|
233
|
+
else:
|
|
234
|
+
opt.tell(args, -fun)
|
|
235
|
+
|
|
236
|
+
if opts:
|
|
237
|
+
for name, opt in opts.items():
|
|
238
|
+
opt_cfg = optimizers[name]
|
|
239
|
+
result = opt.get_result()
|
|
240
|
+
await update_variables(
|
|
241
|
+
variables, {
|
|
242
|
+
name: value
|
|
243
|
+
for name, value in zip(opt_cfg.dimensions.keys(), result.x)
|
|
244
|
+
}, setters)
|
|
245
|
+
|
|
246
|
+
yield variables
|
|
247
|
+
|
|
248
|
+
variables.update(await call_many_functions(order, getters, variables))
|
|
249
|
+
|
|
250
|
+
for key in list(variables.keys()):
|
|
251
|
+
if key.startswith('*') or ',' in key:
|
|
252
|
+
await _unpack(key, variables)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
async def call_many_functions(order: list[list[str]],
|
|
256
|
+
functions: dict[str, Callable],
|
|
257
|
+
variables: dict[str, Any]) -> dict[str, Any]:
|
|
258
|
+
ret = {}
|
|
259
|
+
for group in order:
|
|
260
|
+
waited = []
|
|
261
|
+
coros = []
|
|
262
|
+
for name in group:
|
|
263
|
+
if name in functions:
|
|
264
|
+
waited.append(name)
|
|
265
|
+
coros.append(call_function(functions[name], variables | ret))
|
|
266
|
+
if coros:
|
|
267
|
+
results = await asyncio.gather(*coros)
|
|
268
|
+
ret.update(dict(zip(waited, results)))
|
|
269
|
+
return ret
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
async def _unpack(key, variables):
|
|
273
|
+
x = variables[key]
|
|
274
|
+
if inspect.isawaitable(x):
|
|
275
|
+
x = await x
|
|
276
|
+
if key.startswith('**'):
|
|
277
|
+
assert isinstance(
|
|
278
|
+
x, dict), f"Should promise a dict for `**` symbol. {key}"
|
|
279
|
+
if "{key}" in key:
|
|
280
|
+
for k, v in x.items():
|
|
281
|
+
variables[key[2:].format(key=k)] = v
|
|
282
|
+
else:
|
|
283
|
+
variables.update(x)
|
|
284
|
+
elif key.startswith('*'):
|
|
285
|
+
assert isinstance(
|
|
286
|
+
x, (list, tuple,
|
|
287
|
+
np.ndarray)), f"Should promise a list for `*` symbol. {key}"
|
|
288
|
+
for i, v in enumerate(x):
|
|
289
|
+
k = key[1:].format(i=i)
|
|
290
|
+
variables[k] = v
|
|
291
|
+
elif ',' in key:
|
|
292
|
+
keys1, keys2 = [], []
|
|
293
|
+
args = None
|
|
294
|
+
for k in key.split(','):
|
|
295
|
+
if k.startswith('*'):
|
|
296
|
+
if args is None:
|
|
297
|
+
args = k
|
|
298
|
+
else:
|
|
299
|
+
raise ValueError(f'Only one `*` symbol is allowed. {key}')
|
|
300
|
+
elif args is None:
|
|
301
|
+
keys1.append(k)
|
|
302
|
+
else:
|
|
303
|
+
keys2.append(k)
|
|
304
|
+
assert isinstance(
|
|
305
|
+
x,
|
|
306
|
+
(list, tuple,
|
|
307
|
+
np.ndarray)), f"Should promise a list for multiple symbols. {key}"
|
|
308
|
+
if args is None:
|
|
309
|
+
assert len(keys1) == len(
|
|
310
|
+
x), f"Length of keys and values should be equal. {key}"
|
|
311
|
+
for k, v in zip(keys1, x):
|
|
312
|
+
variables[k] = v
|
|
216
313
|
else:
|
|
217
|
-
|
|
218
|
-
|
|
314
|
+
assert len(keys1) + len(keys2) <= len(
|
|
315
|
+
x), f"Too many values for unpacking. {key}"
|
|
316
|
+
for k, v in zip(keys1, x[:len(keys1)]):
|
|
317
|
+
variables[k] = v
|
|
318
|
+
end = -len(keys2) if keys2 else None
|
|
319
|
+
for i, v in enumerate(x[len(keys1):end]):
|
|
320
|
+
k = args[1:].format(i=i)
|
|
321
|
+
variables[k] = v
|
|
322
|
+
if keys2:
|
|
323
|
+
for k, v in zip(keys2, x[end:]):
|
|
324
|
+
variables[k] = v
|
|
325
|
+
else:
|
|
326
|
+
return
|
|
327
|
+
del variables[key]
|
|
219
328
|
|
|
220
329
|
|
|
221
330
|
class Scan():
|
|
@@ -245,6 +354,7 @@ class Scan():
|
|
|
245
354
|
self.id = task_uuid()
|
|
246
355
|
self.record = None
|
|
247
356
|
self.config = {} if config is None else copy.deepcopy(config)
|
|
357
|
+
self._raw_config_copy = copy.deepcopy(self.config)
|
|
248
358
|
self.description = {
|
|
249
359
|
'app': app,
|
|
250
360
|
'tags': tags,
|
|
@@ -265,7 +375,7 @@ class Scan():
|
|
|
265
375
|
'filters': {},
|
|
266
376
|
'total': {},
|
|
267
377
|
'database': database,
|
|
268
|
-
'hiden': ['self', 'config', r'^__.*', r'.*__$'],
|
|
378
|
+
'hiden': ['self', 'config', r'^__.*', r'.*__$', r'^#.*'],
|
|
269
379
|
'entry': {
|
|
270
380
|
'system': get_system_info(),
|
|
271
381
|
'env': {},
|
|
@@ -277,6 +387,7 @@ class Scan():
|
|
|
277
387
|
self._current_level = 0
|
|
278
388
|
self._variables = {}
|
|
279
389
|
self._main_task = None
|
|
390
|
+
self._background_tasks = ()
|
|
280
391
|
self._sock = None
|
|
281
392
|
self._sem = asyncio.Semaphore(max_promise + 1)
|
|
282
393
|
self._bar: dict[int, tqdm] = {}
|
|
@@ -300,6 +411,7 @@ class Scan():
|
|
|
300
411
|
del state['record']
|
|
301
412
|
del state['_sock']
|
|
302
413
|
del state['_main_task']
|
|
414
|
+
del state['_background_tasks']
|
|
303
415
|
del state['_bar']
|
|
304
416
|
del state['_msg_queue']
|
|
305
417
|
del state['_prm_queue']
|
|
@@ -312,6 +424,7 @@ class Scan():
|
|
|
312
424
|
self.record = None
|
|
313
425
|
self._sock = None
|
|
314
426
|
self._main_task = None
|
|
427
|
+
self._background_tasks = ()
|
|
315
428
|
self._bar = {}
|
|
316
429
|
self._prm_queue = asyncio.Queue()
|
|
317
430
|
self._msg_queue = asyncio.Queue(self._max_message)
|
|
@@ -396,7 +509,7 @@ class Scan():
|
|
|
396
509
|
|
|
397
510
|
if self.config:
|
|
398
511
|
self.description['config'] = await create_config(
|
|
399
|
-
self.
|
|
512
|
+
self._raw_config_copy, self.description['database'], self._sock)
|
|
400
513
|
if current_notebook() is None:
|
|
401
514
|
await create_notebook('untitle', self.description['database'],
|
|
402
515
|
self._sock)
|
|
@@ -412,7 +525,11 @@ class Scan():
|
|
|
412
525
|
'method':
|
|
413
526
|
'record_create',
|
|
414
527
|
'description':
|
|
415
|
-
|
|
528
|
+
dump_dict(self.description,
|
|
529
|
+
keys=[
|
|
530
|
+
'intrinsic_loops', 'app', 'tags', 'loops',
|
|
531
|
+
'independent_variables', 'axis', 'config', 'entry'
|
|
532
|
+
])
|
|
416
533
|
})
|
|
417
534
|
|
|
418
535
|
record_id = await self._sock.recv_pyobj()
|
|
@@ -470,9 +587,9 @@ class Scan():
|
|
|
470
587
|
if depends:
|
|
471
588
|
self.add_depends(name, depends)
|
|
472
589
|
s = ','.join(depends)
|
|
473
|
-
self.description['functions'][f'
|
|
590
|
+
self.description['functions'][f'#{name}'] = value
|
|
474
591
|
self.description['functions'][name] = eval(
|
|
475
|
-
f"lambda self, {s}: self.description['functions']['
|
|
592
|
+
f"lambda self, {s}: self.description['functions']['#{name}']({s})"
|
|
476
593
|
)
|
|
477
594
|
else:
|
|
478
595
|
self.add_depends(name, _get_depends(value))
|
|
@@ -612,27 +729,33 @@ class Scan():
|
|
|
612
729
|
await task
|
|
613
730
|
self._msg_queue.task_done()
|
|
614
731
|
|
|
732
|
+
@contextlib.asynccontextmanager
|
|
733
|
+
async def _send_msg_and_update_bar(self):
|
|
734
|
+
send_msg_task = asyncio.create_task(self._send_msg())
|
|
735
|
+
update_progress_task = asyncio.create_task(self._update_progress())
|
|
736
|
+
try:
|
|
737
|
+
yield (send_msg_task, update_progress_task)
|
|
738
|
+
finally:
|
|
739
|
+
update_progress_task.cancel()
|
|
740
|
+
send_msg_task.cancel()
|
|
741
|
+
while True:
|
|
742
|
+
try:
|
|
743
|
+
task = self._prm_queue.get_nowait()
|
|
744
|
+
except:
|
|
745
|
+
break
|
|
746
|
+
try:
|
|
747
|
+
task.cancel()
|
|
748
|
+
except:
|
|
749
|
+
pass
|
|
750
|
+
|
|
751
|
+
async def _check_background_tasks(self):
|
|
752
|
+
for task in self._background_tasks:
|
|
753
|
+
if task.done():
|
|
754
|
+
await task
|
|
755
|
+
|
|
615
756
|
async def run(self):
|
|
616
757
|
assymbly(self.description)
|
|
617
|
-
|
|
618
|
-
@contextlib.asynccontextmanager
|
|
619
|
-
async def send_msg_and_update_bar(self):
|
|
620
|
-
send_msg_task = asyncio.create_task(self._send_msg())
|
|
621
|
-
update_progress_task = asyncio.create_task(self._update_progress())
|
|
622
|
-
try:
|
|
623
|
-
yield
|
|
624
|
-
finally:
|
|
625
|
-
update_progress_task.cancel()
|
|
626
|
-
send_msg_task.cancel()
|
|
627
|
-
while True:
|
|
628
|
-
try:
|
|
629
|
-
task = self._prm_queue.get_nowait()
|
|
630
|
-
except:
|
|
631
|
-
break
|
|
632
|
-
try:
|
|
633
|
-
task.cancel()
|
|
634
|
-
except:
|
|
635
|
-
pass
|
|
758
|
+
self._background_tasks = ()
|
|
636
759
|
|
|
637
760
|
if isinstance(
|
|
638
761
|
self.description['database'],
|
|
@@ -641,12 +764,14 @@ class Scan():
|
|
|
641
764
|
connect=self.description['database'],
|
|
642
765
|
socket=self._sock) as socket:
|
|
643
766
|
self._sock = socket
|
|
644
|
-
async with
|
|
767
|
+
async with self._send_msg_and_update_bar() as background_tasks:
|
|
768
|
+
self._background_tasks = background_tasks
|
|
645
769
|
await self._run()
|
|
646
770
|
else:
|
|
647
771
|
if self.config:
|
|
648
|
-
self.description['config'] =
|
|
649
|
-
async with
|
|
772
|
+
self.description['config'] = self._raw_config_copy
|
|
773
|
+
async with self._send_msg_and_update_bar() as background_tasks:
|
|
774
|
+
self._background_tasks = background_tasks
|
|
650
775
|
await self._run()
|
|
651
776
|
|
|
652
777
|
async def _run(self):
|
|
@@ -671,11 +796,11 @@ class Scan():
|
|
|
671
796
|
self.description['functions'], self.variables)
|
|
672
797
|
await update_variables(self.variables, updates,
|
|
673
798
|
self.description['setters'])
|
|
674
|
-
|
|
799
|
+
await self._check_background_tasks()
|
|
675
800
|
await self.work()
|
|
676
801
|
for level, bar in self._bar.items():
|
|
677
802
|
bar.close()
|
|
678
|
-
|
|
803
|
+
await self._check_background_tasks()
|
|
679
804
|
if self._single_step:
|
|
680
805
|
self.variables.update(await call_many_functions(
|
|
681
806
|
self.description['order'].get(-1, []),
|
|
@@ -683,7 +808,7 @@ class Scan():
|
|
|
683
808
|
|
|
684
809
|
await self.emit(0, 0, 0, self.variables)
|
|
685
810
|
await self.emit(-1, 0, 0, {})
|
|
686
|
-
|
|
811
|
+
await self._check_background_tasks()
|
|
687
812
|
await self._prm_queue.join()
|
|
688
813
|
await self._msg_queue.join()
|
|
689
814
|
return self.variables
|
|
@@ -747,6 +872,7 @@ class Scan():
|
|
|
747
872
|
| {'config': self._synchronize_config},
|
|
748
873
|
self.description['optimizers'], self.description['setters'],
|
|
749
874
|
self.description['getters']):
|
|
875
|
+
await self._check_background_tasks()
|
|
750
876
|
self._current_level += 1
|
|
751
877
|
if await self._filter(variables, self.current_level - 1):
|
|
752
878
|
yield variables
|
|
@@ -758,11 +884,13 @@ class Scan():
|
|
|
758
884
|
self._current_level -= 1
|
|
759
885
|
self._prm_queue.put_nowait(
|
|
760
886
|
self._update_progress_bar(self.current_level, 1))
|
|
887
|
+
await self._check_background_tasks()
|
|
761
888
|
if self.current_level == 0:
|
|
762
889
|
await self.emit(self.current_level - 1, 0, 0, {})
|
|
763
890
|
for name, value in self.variables.items():
|
|
764
891
|
if inspect.isawaitable(value):
|
|
765
892
|
self.variables[name] = await value
|
|
893
|
+
await self._check_background_tasks()
|
|
766
894
|
await self._prm_queue.join()
|
|
767
895
|
|
|
768
896
|
async def work(self, **kwds):
|
|
@@ -823,7 +951,7 @@ class Scan():
|
|
|
823
951
|
return await awaitable
|
|
824
952
|
|
|
825
953
|
|
|
826
|
-
def
|
|
954
|
+
def _get_environment(description):
|
|
827
955
|
import __main__
|
|
828
956
|
from IPython import get_ipython
|
|
829
957
|
|
|
@@ -853,6 +981,10 @@ def assymbly(description):
|
|
|
853
981
|
|
|
854
982
|
description['entry']['env'] = {k: v for k, v in os.environ.items()}
|
|
855
983
|
|
|
984
|
+
return description
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
def _mapping_levels(description):
|
|
856
988
|
mapping = {
|
|
857
989
|
label: level
|
|
858
990
|
for level, label in enumerate(
|
|
@@ -885,8 +1017,23 @@ def assymbly(description):
|
|
|
885
1017
|
len(space))
|
|
886
1018
|
except:
|
|
887
1019
|
pass
|
|
1020
|
+
return levels
|
|
1021
|
+
|
|
888
1022
|
|
|
1023
|
+
def _get_independent_variables(description):
|
|
1024
|
+
independent_variables = set(description['intrinsic_loops'].keys())
|
|
1025
|
+
for level, loops in description['loops'].items():
|
|
1026
|
+
for name, iterable in loops:
|
|
1027
|
+
if isinstance(iterable, (np.ndarray, list, tuple, range, Space)):
|
|
1028
|
+
independent_variables.add(name)
|
|
1029
|
+
return independent_variables
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
def _build_dependents(description, levels, independent_variables):
|
|
889
1033
|
dependents = copy.deepcopy(description['dependents'])
|
|
1034
|
+
all_nodes = set(description['dependents'].keys())
|
|
1035
|
+
for key, deps in dependents.items():
|
|
1036
|
+
all_nodes.update(deps)
|
|
890
1037
|
|
|
891
1038
|
for level in levels:
|
|
892
1039
|
range_list = description['loops'].get(level, [])
|
|
@@ -899,6 +1046,13 @@ def assymbly(description):
|
|
|
899
1046
|
dependents[name] = set()
|
|
900
1047
|
dependents[name].add(f'#__loop_{level}')
|
|
901
1048
|
|
|
1049
|
+
after_yield = set()
|
|
1050
|
+
for key in all_nodes:
|
|
1051
|
+
if key not in independent_variables and key not in description[
|
|
1052
|
+
'consts']:
|
|
1053
|
+
if key not in dependents:
|
|
1054
|
+
after_yield.add(key)
|
|
1055
|
+
|
|
902
1056
|
def _get_all_depends(key, graph):
|
|
903
1057
|
ret = set()
|
|
904
1058
|
if key not in graph:
|
|
@@ -912,7 +1066,13 @@ def assymbly(description):
|
|
|
912
1066
|
full_depends = {}
|
|
913
1067
|
for key in dependents:
|
|
914
1068
|
full_depends[key] = _get_all_depends(key, dependents)
|
|
1069
|
+
if full_depends[key] & after_yield:
|
|
1070
|
+
after_yield.add(key)
|
|
1071
|
+
|
|
1072
|
+
return dependents, full_depends, after_yield
|
|
1073
|
+
|
|
915
1074
|
|
|
1075
|
+
def _build_order(description, levels, dependents, full_depends):
|
|
916
1076
|
levels = {}
|
|
917
1077
|
passed = set()
|
|
918
1078
|
all_keys = set(description['consts'].keys())
|
|
@@ -957,8 +1117,9 @@ def assymbly(description):
|
|
|
957
1117
|
description['order'][level].append(ready)
|
|
958
1118
|
keys -= set(ready)
|
|
959
1119
|
|
|
1120
|
+
|
|
1121
|
+
def _make_axis(description):
|
|
960
1122
|
axis = {}
|
|
961
|
-
independent_variables = set(description['intrinsic_loops'].keys())
|
|
962
1123
|
|
|
963
1124
|
for name in description['consts']:
|
|
964
1125
|
axis[name] = ()
|
|
@@ -967,8 +1128,6 @@ def assymbly(description):
|
|
|
967
1128
|
if isinstance(iterable, OptimizeSpace):
|
|
968
1129
|
axis[name] = tuple(range(level + 1))
|
|
969
1130
|
continue
|
|
970
|
-
elif isinstance(iterable, (np.ndarray, list, tuple, range, Space)):
|
|
971
|
-
independent_variables.add(name)
|
|
972
1131
|
axis[name] = (level, )
|
|
973
1132
|
|
|
974
1133
|
for level, group in description['order'].items():
|
|
@@ -989,176 +1148,19 @@ def assymbly(description):
|
|
|
989
1148
|
k: tuple([x for x in v if x >= 0])
|
|
990
1149
|
for k, v in axis.items()
|
|
991
1150
|
}
|
|
992
|
-
description['independent_variables'] = independent_variables
|
|
993
|
-
|
|
994
|
-
return description
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
async def update_variables(variables: dict[str, Any], updates: dict[str, Any],
|
|
998
|
-
setters: dict[str, Callable]):
|
|
999
|
-
coros = []
|
|
1000
|
-
for name, value in updates.items():
|
|
1001
|
-
if name in setters:
|
|
1002
|
-
coro = setters[name](value)
|
|
1003
|
-
if inspect.isawaitable(coro):
|
|
1004
|
-
coros.append(coro)
|
|
1005
|
-
variables[name] = value
|
|
1006
|
-
if coros:
|
|
1007
|
-
await asyncio.gather(*coros)
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
async def _iter_level(variables,
|
|
1011
|
-
iters: list[tuple[str, Iterable | Expression | Callable
|
|
1012
|
-
| OptimizeSpace]],
|
|
1013
|
-
order: list[list[str]],
|
|
1014
|
-
functions: dict[str, Callable | Expression],
|
|
1015
|
-
optimizers: dict[str, Optimizer],
|
|
1016
|
-
setters: dict[str, Callable] = {},
|
|
1017
|
-
getters: dict[str, Callable] = {}):
|
|
1018
|
-
iters_d = {}
|
|
1019
|
-
env = Env()
|
|
1020
|
-
env.variables = variables
|
|
1021
|
-
opts = {}
|
|
1022
1151
|
|
|
1023
|
-
for name, iter in iters:
|
|
1024
|
-
if isinstance(iter, OptimizeSpace):
|
|
1025
|
-
if iter.optimizer.name not in opts:
|
|
1026
|
-
opts[iter.optimizer.name] = iter.optimizer.create()
|
|
1027
|
-
elif isinstance(iter, Expression):
|
|
1028
|
-
iters_d[name] = iter.eval(env)
|
|
1029
|
-
elif isinstance(iter, Space):
|
|
1030
|
-
iters_d[name] = iter.toarray()
|
|
1031
|
-
elif callable(iter):
|
|
1032
|
-
iters_d[name] = await call_function(iter, variables)
|
|
1033
|
-
else:
|
|
1034
|
-
iters_d[name] = iter
|
|
1035
|
-
|
|
1036
|
-
maxiter = 0xffffffff
|
|
1037
|
-
for name, opt in opts.items():
|
|
1038
|
-
opt_cfg = optimizers[name]
|
|
1039
|
-
maxiter = min(maxiter, opt_cfg.maxiter)
|
|
1040
|
-
|
|
1041
|
-
async for args in async_zip(*iters_d.values(), range(maxiter)):
|
|
1042
|
-
await update_variables(variables, dict(zip(iters_d.keys(), args[:-1])),
|
|
1043
|
-
setters)
|
|
1044
|
-
for name, opt in opts.items():
|
|
1045
|
-
args = opt.ask()
|
|
1046
|
-
opt_cfg = optimizers[name]
|
|
1047
|
-
await update_variables(variables, {
|
|
1048
|
-
n: v
|
|
1049
|
-
for n, v in zip(opt_cfg.dimensions.keys(), args)
|
|
1050
|
-
}, setters)
|
|
1051
|
-
|
|
1052
|
-
await update_variables(
|
|
1053
|
-
variables, await call_many_functions(order, functions, variables),
|
|
1054
|
-
setters)
|
|
1055
|
-
|
|
1056
|
-
yield variables
|
|
1057
|
-
|
|
1058
|
-
variables.update(await call_many_functions(order, getters, variables))
|
|
1059
|
-
|
|
1060
|
-
if opts:
|
|
1061
|
-
for key in list(variables.keys()):
|
|
1062
|
-
if key.startswith('*') or ',' in key:
|
|
1063
|
-
await _unpack(key, variables)
|
|
1064
|
-
|
|
1065
|
-
for name, opt in opts.items():
|
|
1066
|
-
opt_cfg = optimizers[name]
|
|
1067
|
-
args = [variables[n] for n in opt_cfg.dimensions.keys()]
|
|
1068
|
-
|
|
1069
|
-
if name not in variables:
|
|
1070
|
-
raise ValueError(f'{name} not in variables.')
|
|
1071
|
-
fun = variables[name]
|
|
1072
|
-
if inspect.isawaitable(fun):
|
|
1073
|
-
fun = await fun
|
|
1074
|
-
if opt_cfg.minimize:
|
|
1075
|
-
opt.tell(args, fun)
|
|
1076
|
-
else:
|
|
1077
|
-
opt.tell(args, -fun)
|
|
1078
|
-
|
|
1079
|
-
for name, opt in opts.items():
|
|
1080
|
-
opt_cfg = optimizers[name]
|
|
1081
|
-
result = opt.get_result()
|
|
1082
|
-
await update_variables(
|
|
1083
|
-
variables, {
|
|
1084
|
-
name: value
|
|
1085
|
-
for name, value in zip(opt_cfg.dimensions.keys(), result.x)
|
|
1086
|
-
}, setters)
|
|
1087
|
-
variables[name] = result.fun
|
|
1088
|
-
if opts:
|
|
1089
|
-
yield variables
|
|
1090
1152
|
|
|
1153
|
+
def assymbly(description):
|
|
1154
|
+
_get_environment(description)
|
|
1155
|
+
levels = _mapping_levels(description)
|
|
1156
|
+
independent_variables = _get_independent_variables(description)
|
|
1157
|
+
description['independent_variables'] = independent_variables
|
|
1091
1158
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
variables: dict[str, Any]) -> dict[str, Any]:
|
|
1095
|
-
ret = {}
|
|
1096
|
-
for group in order:
|
|
1097
|
-
waited = []
|
|
1098
|
-
coros = []
|
|
1099
|
-
for name in group:
|
|
1100
|
-
if name in functions:
|
|
1101
|
-
waited.append(name)
|
|
1102
|
-
coros.append(call_function(functions[name], variables | ret))
|
|
1103
|
-
if coros:
|
|
1104
|
-
results = await asyncio.gather(*coros)
|
|
1105
|
-
ret.update(dict(zip(waited, results)))
|
|
1106
|
-
return ret
|
|
1159
|
+
dependents, full_depends, after_yield = _build_dependents(
|
|
1160
|
+
description, levels, independent_variables)
|
|
1107
1161
|
|
|
1162
|
+
_build_order(description, levels, dependents, full_depends)
|
|
1163
|
+
_make_axis(description)
|
|
1108
1164
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
if inspect.isawaitable(x):
|
|
1112
|
-
x = await x
|
|
1113
|
-
if key.startswith('**'):
|
|
1114
|
-
assert isinstance(
|
|
1115
|
-
x, dict), f"Should promise a dict for `**` symbol. {key}"
|
|
1116
|
-
if "{key}" in key:
|
|
1117
|
-
for k, v in x.items():
|
|
1118
|
-
variables[key[2:].format(key=k)] = v
|
|
1119
|
-
else:
|
|
1120
|
-
variables.update(x)
|
|
1121
|
-
elif key.startswith('*'):
|
|
1122
|
-
assert isinstance(
|
|
1123
|
-
x, (list, tuple,
|
|
1124
|
-
np.ndarray)), f"Should promise a list for `*` symbol. {key}"
|
|
1125
|
-
for i, v in enumerate(x):
|
|
1126
|
-
k = key[1:].format(i=i)
|
|
1127
|
-
variables[k] = v
|
|
1128
|
-
elif ',' in key:
|
|
1129
|
-
keys1, keys2 = [], []
|
|
1130
|
-
args = None
|
|
1131
|
-
for k in key.split(','):
|
|
1132
|
-
if k.startswith('*'):
|
|
1133
|
-
if args is None:
|
|
1134
|
-
args = k
|
|
1135
|
-
else:
|
|
1136
|
-
raise ValueError(f'Only one `*` symbol is allowed. {key}')
|
|
1137
|
-
elif args is None:
|
|
1138
|
-
keys1.append(k)
|
|
1139
|
-
else:
|
|
1140
|
-
keys2.append(k)
|
|
1141
|
-
assert isinstance(
|
|
1142
|
-
x,
|
|
1143
|
-
(list, tuple,
|
|
1144
|
-
np.ndarray)), f"Should promise a list for multiple symbols. {key}"
|
|
1145
|
-
if args is None:
|
|
1146
|
-
assert len(keys1) == len(
|
|
1147
|
-
x), f"Length of keys and values should be equal. {key}"
|
|
1148
|
-
for k, v in zip(keys1, x):
|
|
1149
|
-
variables[k] = v
|
|
1150
|
-
else:
|
|
1151
|
-
assert len(keys1) + len(keys2) <= len(
|
|
1152
|
-
x), f"Too many values for unpacking. {key}"
|
|
1153
|
-
for k, v in zip(keys1, x[:len(keys1)]):
|
|
1154
|
-
variables[k] = v
|
|
1155
|
-
end = -len(keys2) if keys2 else None
|
|
1156
|
-
for i, v in enumerate(x[len(keys1):end]):
|
|
1157
|
-
k = args[1:].format(i=i)
|
|
1158
|
-
variables[k] = v
|
|
1159
|
-
if keys2:
|
|
1160
|
-
for k, v in zip(keys2, x[end:]):
|
|
1161
|
-
variables[k] = v
|
|
1162
|
-
else:
|
|
1163
|
-
return
|
|
1164
|
-
del variables[key]
|
|
1165
|
+
return description
|
|
1166
|
+
return description
|
qulab/scan/server.py
CHANGED
|
@@ -20,6 +20,7 @@ from .models import Cell, Notebook
|
|
|
20
20
|
from .models import Record as RecordInDB
|
|
21
21
|
from .models import Session, create_engine, create_tables, sessionmaker, utcnow
|
|
22
22
|
from .record import BufferList, Record, random_path
|
|
23
|
+
from .utils import dump_dict, load_dict
|
|
23
24
|
|
|
24
25
|
try:
|
|
25
26
|
default_record_port = int(os.getenv('QULAB_RECORD_PORT', 6789))
|
|
@@ -53,6 +54,16 @@ class Request():
|
|
|
53
54
|
return f"Request({self.method})"
|
|
54
55
|
|
|
55
56
|
|
|
57
|
+
class Response():
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ErrorResponse(Response):
|
|
62
|
+
|
|
63
|
+
def __init__(self, error):
|
|
64
|
+
self.error = error
|
|
65
|
+
|
|
66
|
+
|
|
56
67
|
async def reply(req, resp):
|
|
57
68
|
await req.sock.send_multipart([req.identity, pickle.dumps(resp)])
|
|
58
69
|
|
|
@@ -62,14 +73,15 @@ def clear_cache():
|
|
|
62
73
|
return
|
|
63
74
|
|
|
64
75
|
logger.debug(f"clear_cache record_cache: {len(record_cache)}")
|
|
65
|
-
for k, (t,
|
|
66
|
-
|
|
76
|
+
for (k, (t, r),
|
|
77
|
+
i) in zip(sorted(record_cache.items(), key=lambda x: x[1][0]),
|
|
78
|
+
range(len(record_cache) - CACHE_SIZE)):
|
|
67
79
|
del record_cache[k]
|
|
68
80
|
|
|
69
81
|
logger.debug(f"clear_cache buffer_list_cache: {len(buffer_list_cache)}")
|
|
70
|
-
for k, (t,
|
|
71
|
-
|
|
72
|
-
|
|
82
|
+
for (k, (t, r),
|
|
83
|
+
i) in zip(sorted(buffer_list_cache.items(), key=lambda x: x[1][0]),
|
|
84
|
+
range(len(buffer_list_cache) - CACHE_SIZE)):
|
|
73
85
|
del buffer_list_cache[k]
|
|
74
86
|
logger.debug(f"clear_cache done.")
|
|
75
87
|
|
|
@@ -173,7 +185,7 @@ def record_delete(session: Session, record_id: int, datapath: Path):
|
|
|
173
185
|
session.commit()
|
|
174
186
|
|
|
175
187
|
|
|
176
|
-
@logger.catch
|
|
188
|
+
@logger.catch(reraise=True)
|
|
177
189
|
async def handle(session: Session, request: Request, datapath: Path):
|
|
178
190
|
|
|
179
191
|
msg = request.msg
|
|
@@ -222,7 +234,7 @@ async def handle(session: Session, request: Request, datapath: Path):
|
|
|
222
234
|
logger.debug(f"end bufferlist_iter_exit: {msg}")
|
|
223
235
|
case 'record_create':
|
|
224
236
|
logger.debug(f"record_create")
|
|
225
|
-
description =
|
|
237
|
+
description = load_dict(msg['description'])
|
|
226
238
|
await reply(request, record_create(session, description, datapath))
|
|
227
239
|
logger.debug(f"reply record_create")
|
|
228
240
|
case 'record_append':
|
|
@@ -349,29 +361,16 @@ async def handle_with_timeout(session: Session, request: Request,
|
|
|
349
361
|
f"Task handling request {request} timed out and was cancelled.")
|
|
350
362
|
await reply(request, 'timeout')
|
|
351
363
|
except Exception as e:
|
|
352
|
-
|
|
364
|
+
logger.error(f"Task handling request {request} failed: {e!r}")
|
|
365
|
+
await reply(request, ErrorResponse(f'{e!r}'))
|
|
366
|
+
logger.debug(f"Task handling request {request} finished.")
|
|
353
367
|
|
|
354
368
|
|
|
355
369
|
async def serv(port,
|
|
356
370
|
datapath,
|
|
357
371
|
url='',
|
|
358
372
|
buffer_size=1024 * 1024 * 1024,
|
|
359
|
-
interval=60
|
|
360
|
-
log='stderr',
|
|
361
|
-
debug=False):
|
|
362
|
-
if debug:
|
|
363
|
-
level = 'DEBUG'
|
|
364
|
-
else:
|
|
365
|
-
level = 'INFO'
|
|
366
|
-
|
|
367
|
-
if log == 'stderr':
|
|
368
|
-
pass
|
|
369
|
-
#logger.add(sys.stderr, level=level)
|
|
370
|
-
elif log == 'stdout':
|
|
371
|
-
logger.add(sys.stdout, level=level)
|
|
372
|
-
else:
|
|
373
|
-
logger.add(log, level=level)
|
|
374
|
-
|
|
373
|
+
interval=60):
|
|
375
374
|
logger.debug('Creating socket...')
|
|
376
375
|
async with ZMQContextManager(zmq.ROUTER, bind=f"tcp://*:{port}") as sock:
|
|
377
376
|
logger.info(f'Server started at port {port}.')
|
|
@@ -392,8 +391,11 @@ async def serv(port,
|
|
|
392
391
|
received += len(msg)
|
|
393
392
|
try:
|
|
394
393
|
req = Request(sock, identity, msg)
|
|
395
|
-
except:
|
|
394
|
+
except Exception as e:
|
|
396
395
|
logger.exception('bad request')
|
|
396
|
+
await sock.send_multipart(
|
|
397
|
+
[identity,
|
|
398
|
+
pickle.dumps(ErrorResponse(f'{e!r}'))])
|
|
397
399
|
continue
|
|
398
400
|
asyncio.create_task(
|
|
399
401
|
handle_with_timeout(session, req, datapath,
|
|
@@ -415,9 +417,21 @@ async def main(port,
|
|
|
415
417
|
no_watch=True,
|
|
416
418
|
debug=False):
|
|
417
419
|
if no_watch:
|
|
420
|
+
logger.remove()
|
|
421
|
+
if debug:
|
|
422
|
+
level = 'DEBUG'
|
|
423
|
+
else:
|
|
424
|
+
level = 'INFO'
|
|
425
|
+
if log == 'stderr':
|
|
426
|
+
logger.add(sys.stderr, level=level)
|
|
427
|
+
elif log == 'stdout':
|
|
428
|
+
logger.add(sys.stdout, level=level)
|
|
429
|
+
else:
|
|
430
|
+
logger.add(sys.stderr, level=level)
|
|
431
|
+
logger.add(log, level=level)
|
|
432
|
+
logger.debug(f"logging level: {level}")
|
|
418
433
|
logger.info('Server starting...')
|
|
419
|
-
await serv(port, datapath, url, buffer * 1024 * 1024, interval
|
|
420
|
-
debug)
|
|
434
|
+
await serv(port, datapath, url, buffer * 1024 * 1024, interval)
|
|
421
435
|
else:
|
|
422
436
|
process = None
|
|
423
437
|
|
|
@@ -444,10 +458,24 @@ async def main(port,
|
|
|
444
458
|
f'killed process. PID={process.pid}, returncode={process.returncode}'
|
|
445
459
|
)
|
|
446
460
|
cmd = [
|
|
447
|
-
sys.executable,
|
|
448
|
-
|
|
449
|
-
"
|
|
450
|
-
"
|
|
461
|
+
sys.executable,
|
|
462
|
+
"-m",
|
|
463
|
+
"qulab",
|
|
464
|
+
"server",
|
|
465
|
+
"--port",
|
|
466
|
+
f"{port}",
|
|
467
|
+
"--datapath",
|
|
468
|
+
f"{datapath}",
|
|
469
|
+
"--url",
|
|
470
|
+
f"{url}",
|
|
471
|
+
"--timeout",
|
|
472
|
+
f"{timeout}",
|
|
473
|
+
"--buffer",
|
|
474
|
+
f"{buffer}",
|
|
475
|
+
"--interval",
|
|
476
|
+
f"{interval}",
|
|
477
|
+
"--log",
|
|
478
|
+
f"{log}",
|
|
451
479
|
]
|
|
452
480
|
if url:
|
|
453
481
|
cmd.extend(['--url', url])
|
|
@@ -455,10 +483,7 @@ async def main(port,
|
|
|
455
483
|
cmd.append('--debug')
|
|
456
484
|
cmd.append("--no-watch")
|
|
457
485
|
logger.debug(f"starting process: {' '.join(cmd)}")
|
|
458
|
-
process = subprocess.Popen(cmd,
|
|
459
|
-
stdout=subprocess.PIPE,
|
|
460
|
-
stderr=subprocess.PIPE,
|
|
461
|
-
cwd=os.getcwd())
|
|
486
|
+
process = subprocess.Popen(cmd, cwd=os.getcwd())
|
|
462
487
|
logger.debug(
|
|
463
488
|
f'process started. PID={process.pid}, returncode={process.returncode}'
|
|
464
489
|
)
|
|
@@ -490,9 +515,15 @@ async def main(port,
|
|
|
490
515
|
@click.option('--debug', is_flag=True, help='Debug mode.')
|
|
491
516
|
def server(port, datapath, url, timeout, buffer, interval, log, no_watch,
|
|
492
517
|
debug):
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
518
|
+
try:
|
|
519
|
+
import uvloop
|
|
520
|
+
uvloop.run(
|
|
521
|
+
main(port, Path(datapath), url, timeout, buffer, interval, log,
|
|
522
|
+
True, debug))
|
|
523
|
+
except ImportError:
|
|
524
|
+
asyncio.run(
|
|
525
|
+
main(port, Path(datapath), url, timeout, buffer, interval, log,
|
|
526
|
+
True, debug))
|
|
496
527
|
|
|
497
528
|
|
|
498
529
|
if __name__ == "__main__":
|
qulab/scan/space.py
CHANGED
|
@@ -25,12 +25,27 @@ class Space():
|
|
|
25
25
|
|
|
26
26
|
@classmethod
|
|
27
27
|
def fromarray(cls, space):
|
|
28
|
-
if isinstance(space, Space):
|
|
28
|
+
if isinstance(space, (Space, range, enumerate, tuple)):
|
|
29
29
|
return space
|
|
30
|
-
if isinstance(space,
|
|
31
|
-
|
|
30
|
+
if isinstance(space, list):
|
|
31
|
+
if isinstance(space[0], int):
|
|
32
|
+
try:
|
|
33
|
+
if all(i == j for i, j in zip(
|
|
34
|
+
space,
|
|
35
|
+
range(space[0], space[-1] + 1, space[1] -
|
|
36
|
+
space[0]))):
|
|
37
|
+
return range(space[0], space[-1] + 1,
|
|
38
|
+
space[1] - space[0])
|
|
39
|
+
except:
|
|
40
|
+
return space
|
|
41
|
+
elif isinstance(space[0], (float, complex, np.ndarray)):
|
|
42
|
+
array = np.array(space)
|
|
43
|
+
else:
|
|
44
|
+
return space
|
|
32
45
|
elif isinstance(space, np.ndarray):
|
|
33
46
|
array = space
|
|
47
|
+
else:
|
|
48
|
+
return space
|
|
34
49
|
try:
|
|
35
50
|
a = np.linspace(array[0], array[-1], len(array), dtype=array.dtype)
|
|
36
51
|
if np.allclose(a, array):
|
qulab/scan/utils.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import ast
|
|
2
2
|
import asyncio
|
|
3
3
|
import inspect
|
|
4
|
+
import platform
|
|
5
|
+
import re
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import uuid
|
|
4
9
|
import warnings
|
|
5
10
|
from typing import Any, Callable
|
|
6
11
|
|
|
@@ -29,6 +34,46 @@ class TooLarge:
|
|
|
29
34
|
return f'<TooLarge: {self.type} at 0x{id(self):x}>'
|
|
30
35
|
|
|
31
36
|
|
|
37
|
+
def dump_dict(d, keys=[]):
|
|
38
|
+
ret = {}
|
|
39
|
+
|
|
40
|
+
for key, value in d.items():
|
|
41
|
+
if key in keys:
|
|
42
|
+
ret[key] = value
|
|
43
|
+
continue
|
|
44
|
+
if isinstance(value, dict) and isinstance(key, str):
|
|
45
|
+
ret[key] = dump_dict(value,
|
|
46
|
+
keys=[
|
|
47
|
+
k[len(key) + 1:] for k in keys
|
|
48
|
+
if k.startswith(f'{key}.')
|
|
49
|
+
])
|
|
50
|
+
else:
|
|
51
|
+
try:
|
|
52
|
+
ret[key] = dill.dumps(value)
|
|
53
|
+
except:
|
|
54
|
+
ret[key] = Unpicklable(value)
|
|
55
|
+
|
|
56
|
+
return dill.dumps(ret)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def load_dict(buff):
|
|
60
|
+
if isinstance(buff, dict):
|
|
61
|
+
return {key: load_dict(value) for key, value in buff.items()}
|
|
62
|
+
|
|
63
|
+
if not isinstance(buff, bytes):
|
|
64
|
+
return buff
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
ret = dill.loads(buff)
|
|
68
|
+
except:
|
|
69
|
+
return buff
|
|
70
|
+
|
|
71
|
+
if isinstance(ret, dict):
|
|
72
|
+
return load_dict(ret)
|
|
73
|
+
else:
|
|
74
|
+
return ret
|
|
75
|
+
|
|
76
|
+
|
|
32
77
|
def dump_globals(ns=None, *, size_limit=10 * 1024 * 1024, warn=False):
|
|
33
78
|
import __main__
|
|
34
79
|
|
|
@@ -129,3 +174,56 @@ async def call_function(func: Callable | Expression, variables: dict[str,
|
|
|
129
174
|
if inspect.isawaitable(ret):
|
|
130
175
|
ret = await ret
|
|
131
176
|
return ret
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def yapf_reformat(cell_text):
|
|
180
|
+
try:
|
|
181
|
+
import isort
|
|
182
|
+
import yapf.yapflib.yapf_api
|
|
183
|
+
|
|
184
|
+
fname = f"f{uuid.uuid1().hex}"
|
|
185
|
+
|
|
186
|
+
def wrap(source):
|
|
187
|
+
lines = [f"async def {fname}():"]
|
|
188
|
+
for line in source.split('\n'):
|
|
189
|
+
lines.append(" " + line)
|
|
190
|
+
return '\n'.join(lines)
|
|
191
|
+
|
|
192
|
+
def unwrap(source):
|
|
193
|
+
lines = []
|
|
194
|
+
for line in source.split('\n'):
|
|
195
|
+
if line.startswith(f"async def {fname}():"):
|
|
196
|
+
continue
|
|
197
|
+
lines.append(line[4:])
|
|
198
|
+
return '\n'.join(lines)
|
|
199
|
+
|
|
200
|
+
cell_text = re.sub('^%', '#%#', cell_text, flags=re.M)
|
|
201
|
+
reformated_text = unwrap(
|
|
202
|
+
yapf.yapflib.yapf_api.FormatCode(wrap(isort.code(cell_text)))[0])
|
|
203
|
+
return re.sub('^#%#', '%', reformated_text, flags=re.M)
|
|
204
|
+
except:
|
|
205
|
+
return cell_text
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def get_installed_packages():
|
|
209
|
+
result = subprocess.run([sys.executable, '-m', 'pip', 'freeze'],
|
|
210
|
+
stdout=subprocess.PIPE,
|
|
211
|
+
text=True)
|
|
212
|
+
|
|
213
|
+
lines = result.stdout.split('\n')
|
|
214
|
+
packages = []
|
|
215
|
+
for line in lines:
|
|
216
|
+
if line:
|
|
217
|
+
packages.append(line)
|
|
218
|
+
return packages
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def get_system_info():
|
|
222
|
+
info = {
|
|
223
|
+
'OS': platform.uname()._asdict(),
|
|
224
|
+
'Python': sys.version,
|
|
225
|
+
'PythonExecutable': sys.executable,
|
|
226
|
+
'PythonPath': sys.path,
|
|
227
|
+
'packages': get_installed_packages()
|
|
228
|
+
}
|
|
229
|
+
return info
|
qulab/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.3.
|
|
1
|
+
__version__ = "2.3.5"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|