QuLab 2.0.9__cp311-cp311-macosx_10_9_universal2.whl → 2.1.1__cp311-cp311-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.0.9.dist-info → QuLab-2.1.1.dist-info}/METADATA +1 -1
- {QuLab-2.0.9.dist-info → QuLab-2.1.1.dist-info}/RECORD +16 -14
- qulab/fun.cpython-311-darwin.so +0 -0
- qulab/scan/__init__.py +1 -1
- qulab/scan/{query_record.py → query.py} +8 -4
- qulab/scan/record.py +448 -0
- qulab/scan/recorder.py +4 -410
- qulab/scan/scan.py +219 -215
- qulab/scan/server.py +4 -6
- qulab/scan/space.py +172 -0
- qulab/scan/utils.py +48 -0
- qulab/version.py +1 -1
- {QuLab-2.0.9.dist-info → QuLab-2.1.1.dist-info}/LICENSE +0 -0
- {QuLab-2.0.9.dist-info → QuLab-2.1.1.dist-info}/WHEEL +0 -0
- {QuLab-2.0.9.dist-info → QuLab-2.1.1.dist-info}/entry_points.txt +0 -0
- {QuLab-2.0.9.dist-info → QuLab-2.1.1.dist-info}/top_level.txt +0 -0
qulab/scan/scan.py
CHANGED
|
@@ -1,34 +1,52 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import copy
|
|
3
|
-
import datetime
|
|
4
3
|
import inspect
|
|
5
4
|
import itertools
|
|
6
5
|
import os
|
|
7
6
|
import re
|
|
8
7
|
import sys
|
|
9
8
|
import uuid
|
|
10
|
-
import
|
|
9
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
11
10
|
from graphlib import TopologicalSorter
|
|
12
11
|
from pathlib import Path
|
|
13
|
-
from
|
|
14
|
-
from typing import Any, Awaitable, Callable, Iterable, Type
|
|
12
|
+
from typing import Any, Awaitable, Callable, Iterable
|
|
15
13
|
|
|
16
14
|
import dill
|
|
17
15
|
import numpy as np
|
|
18
|
-
import skopt
|
|
19
16
|
import zmq
|
|
20
|
-
from skopt.space import Categorical, Integer, Real
|
|
21
|
-
from tqdm.notebook import tqdm
|
|
22
17
|
|
|
23
18
|
from ..sys.rpc.zmq_socket import ZMQContextManager
|
|
24
19
|
from .expression import Env, Expression, Symbol
|
|
25
20
|
from .optimize import NgOptimizer
|
|
26
|
-
from .
|
|
27
|
-
from .
|
|
21
|
+
from .record import Record
|
|
22
|
+
from .recorder import default_record_port
|
|
23
|
+
from .space import Optimizer, OptimizeSpace, Space
|
|
24
|
+
from .utils import async_zip, call_function, dump_globals
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
from tqdm.notebook import tqdm
|
|
28
|
+
except:
|
|
29
|
+
|
|
30
|
+
class tqdm():
|
|
31
|
+
|
|
32
|
+
def update(self, n):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def close(self):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
def reset(self):
|
|
39
|
+
pass
|
|
40
|
+
|
|
28
41
|
|
|
29
42
|
__process_uuid = uuid.uuid1()
|
|
30
43
|
__task_counter = itertools.count()
|
|
31
44
|
|
|
45
|
+
if os.getenv('QULAB_SERVER'):
|
|
46
|
+
default_server = os.getenv('QULAB_SERVER')
|
|
47
|
+
else:
|
|
48
|
+
default_server = f'tcp://127.0.0.1:{default_record_port}'
|
|
49
|
+
|
|
32
50
|
|
|
33
51
|
def task_uuid():
|
|
34
52
|
return uuid.uuid3(__process_uuid, str(next(__task_counter)))
|
|
@@ -54,79 +72,6 @@ def _get_depends(func: Callable):
|
|
|
54
72
|
return args
|
|
55
73
|
|
|
56
74
|
|
|
57
|
-
class OptimizeSpace():
|
|
58
|
-
|
|
59
|
-
def __init__(self, optimizer: 'Optimizer', space):
|
|
60
|
-
self.optimizer = optimizer
|
|
61
|
-
self.space = space
|
|
62
|
-
self.name = None
|
|
63
|
-
|
|
64
|
-
def __len__(self):
|
|
65
|
-
return self.optimizer.maxiter
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class Optimizer():
|
|
69
|
-
|
|
70
|
-
def __init__(self,
|
|
71
|
-
scanner: 'Scan',
|
|
72
|
-
name: str,
|
|
73
|
-
level: int,
|
|
74
|
-
method: str | Type = skopt.Optimizer,
|
|
75
|
-
maxiter: int = 1000,
|
|
76
|
-
minimize: bool = True,
|
|
77
|
-
**kwds):
|
|
78
|
-
self.scanner = scanner
|
|
79
|
-
self.method = method
|
|
80
|
-
self.maxiter = maxiter
|
|
81
|
-
self.dimensions = {}
|
|
82
|
-
self.name = name
|
|
83
|
-
self.level = level
|
|
84
|
-
self.kwds = kwds
|
|
85
|
-
self.minimize = minimize
|
|
86
|
-
|
|
87
|
-
def create(self):
|
|
88
|
-
return self.method(list(self.dimensions.values()), **self.kwds)
|
|
89
|
-
|
|
90
|
-
def Categorical(self,
|
|
91
|
-
categories,
|
|
92
|
-
prior=None,
|
|
93
|
-
transform=None,
|
|
94
|
-
name=None) -> OptimizeSpace:
|
|
95
|
-
return OptimizeSpace(self,
|
|
96
|
-
Categorical(categories, prior, transform, name))
|
|
97
|
-
|
|
98
|
-
def Integer(self,
|
|
99
|
-
low,
|
|
100
|
-
high,
|
|
101
|
-
prior="uniform",
|
|
102
|
-
base=10,
|
|
103
|
-
transform=None,
|
|
104
|
-
name=None,
|
|
105
|
-
dtype=np.int64) -> OptimizeSpace:
|
|
106
|
-
return OptimizeSpace(
|
|
107
|
-
self, Integer(low, high, prior, base, transform, name, dtype))
|
|
108
|
-
|
|
109
|
-
def Real(self,
|
|
110
|
-
low,
|
|
111
|
-
high,
|
|
112
|
-
prior="uniform",
|
|
113
|
-
base=10,
|
|
114
|
-
transform=None,
|
|
115
|
-
name=None,
|
|
116
|
-
dtype=float) -> OptimizeSpace:
|
|
117
|
-
return OptimizeSpace(
|
|
118
|
-
self, Real(low, high, prior, base, transform, name, dtype))
|
|
119
|
-
|
|
120
|
-
def __getstate__(self) -> dict:
|
|
121
|
-
state = self.__dict__.copy()
|
|
122
|
-
del state['scanner']
|
|
123
|
-
return state
|
|
124
|
-
|
|
125
|
-
def __setstate__(self, state: dict) -> None:
|
|
126
|
-
self.__dict__.update(state)
|
|
127
|
-
self.scanner = None
|
|
128
|
-
|
|
129
|
-
|
|
130
75
|
class Promise():
|
|
131
76
|
__slots__ = ['task', 'key', 'attr']
|
|
132
77
|
|
|
@@ -157,6 +102,11 @@ class Promise():
|
|
|
157
102
|
return Promise(self.task, None, attr)
|
|
158
103
|
|
|
159
104
|
|
|
105
|
+
def _run_function_in_process(buf):
|
|
106
|
+
func, args, kwds = dill.loads(buf)
|
|
107
|
+
return func(*args, **kwds)
|
|
108
|
+
|
|
109
|
+
|
|
160
110
|
class Scan():
|
|
161
111
|
|
|
162
112
|
def __new__(cls, *args, mixin=None, **kwds):
|
|
@@ -174,8 +124,10 @@ class Scan():
|
|
|
174
124
|
app: str = 'task',
|
|
175
125
|
tags: tuple[str] = (),
|
|
176
126
|
database: str | Path
|
|
177
|
-
| None =
|
|
127
|
+
| None = default_server,
|
|
178
128
|
dump_globals: bool = False,
|
|
129
|
+
max_workers: int = 4,
|
|
130
|
+
max_promise: int = 100,
|
|
179
131
|
mixin=None):
|
|
180
132
|
self.id = task_uuid()
|
|
181
133
|
self.record = None
|
|
@@ -193,6 +145,8 @@ class Scan():
|
|
|
193
145
|
'actions': {},
|
|
194
146
|
'dependents': {},
|
|
195
147
|
'order': {},
|
|
148
|
+
'axis': {},
|
|
149
|
+
'independent_variables': set(),
|
|
196
150
|
'filters': {},
|
|
197
151
|
'total': {},
|
|
198
152
|
'database': database,
|
|
@@ -207,23 +161,21 @@ class Scan():
|
|
|
207
161
|
self._variables = {}
|
|
208
162
|
self._main_task = None
|
|
209
163
|
self._sock = None
|
|
210
|
-
self._sem = asyncio.Semaphore(
|
|
164
|
+
self._sem = asyncio.Semaphore(max_promise + 1)
|
|
211
165
|
self._bar: dict[int, tqdm] = {}
|
|
212
166
|
self._hide_pattern_re = re.compile('|'.join(self.description['hiden']))
|
|
213
|
-
self.
|
|
214
|
-
self.
|
|
167
|
+
self._msg_queue = asyncio.Queue()
|
|
168
|
+
self._prm_queue = asyncio.Queue()
|
|
215
169
|
self._single_step = True
|
|
170
|
+
self._max_workers = max_workers
|
|
171
|
+
self._max_promise = max_promise
|
|
172
|
+
self._executors = ProcessPoolExecutor(max_workers=max_workers)
|
|
216
173
|
|
|
217
174
|
def __del__(self):
|
|
218
175
|
try:
|
|
219
176
|
self._main_task.cancel()
|
|
220
177
|
except:
|
|
221
178
|
pass
|
|
222
|
-
for task in self._task_pool:
|
|
223
|
-
try:
|
|
224
|
-
task.cancel()
|
|
225
|
-
except:
|
|
226
|
-
pass
|
|
227
179
|
|
|
228
180
|
def __getstate__(self) -> dict:
|
|
229
181
|
state = self.__dict__.copy()
|
|
@@ -231,9 +183,10 @@ class Scan():
|
|
|
231
183
|
del state['_sock']
|
|
232
184
|
del state['_main_task']
|
|
233
185
|
del state['_bar']
|
|
234
|
-
del state['
|
|
235
|
-
del state['
|
|
186
|
+
del state['_msg_queue']
|
|
187
|
+
del state['_prm_queue']
|
|
236
188
|
del state['_sem']
|
|
189
|
+
del state['_executors']
|
|
237
190
|
return state
|
|
238
191
|
|
|
239
192
|
def __setstate__(self, state: dict) -> None:
|
|
@@ -242,12 +195,20 @@ class Scan():
|
|
|
242
195
|
self._sock = None
|
|
243
196
|
self._main_task = None
|
|
244
197
|
self._bar = {}
|
|
245
|
-
self.
|
|
246
|
-
self.
|
|
247
|
-
self._sem = asyncio.Semaphore(
|
|
198
|
+
self._prm_queue = asyncio.Queue()
|
|
199
|
+
self._msg_queue = asyncio.Queue()
|
|
200
|
+
self._sem = asyncio.Semaphore(self._max_promise + 1)
|
|
201
|
+
self._executors = ProcessPoolExecutor(max_workers=self._max_workers)
|
|
248
202
|
for opt in self.description['optimizers'].values():
|
|
249
203
|
opt.scanner = self
|
|
250
204
|
|
|
205
|
+
def __del__(self):
|
|
206
|
+
try:
|
|
207
|
+
self._main_task.cancel()
|
|
208
|
+
except:
|
|
209
|
+
pass
|
|
210
|
+
self._executors.shutdown()
|
|
211
|
+
|
|
251
212
|
@property
|
|
252
213
|
def current_level(self):
|
|
253
214
|
return self._current_level
|
|
@@ -256,8 +217,8 @@ class Scan():
|
|
|
256
217
|
def variables(self) -> dict[str, Any]:
|
|
257
218
|
return self._variables
|
|
258
219
|
|
|
259
|
-
async def
|
|
260
|
-
|
|
220
|
+
async def _emit(self, current_level, step, position, variables: dict[str,
|
|
221
|
+
Any]):
|
|
261
222
|
for key, value in list(variables.items()):
|
|
262
223
|
if inspect.isawaitable(value) and not self.hiden(key):
|
|
263
224
|
variables[key] = await value
|
|
@@ -280,6 +241,11 @@ class Scan():
|
|
|
280
241
|
for k, v in variables.items() if not self.hiden(k)
|
|
281
242
|
})
|
|
282
243
|
|
|
244
|
+
def emit(self, current_level, step, position, variables: dict[str, Any]):
|
|
245
|
+
self._msg_queue.put_nowait(
|
|
246
|
+
asyncio.create_task(
|
|
247
|
+
self._emit(current_level, step, position, variables.copy())))
|
|
248
|
+
|
|
283
249
|
def hide(self, name: str):
|
|
284
250
|
self.description['hiden'].append(name)
|
|
285
251
|
self._hide_pattern_re = re.compile('|'.join(self.description['hiden']))
|
|
@@ -289,11 +255,11 @@ class Scan():
|
|
|
289
255
|
|
|
290
256
|
async def _filter(self, variables: dict[str, Any], level: int = 0):
|
|
291
257
|
try:
|
|
292
|
-
return all([
|
|
293
|
-
|
|
258
|
+
return all(await asyncio.gather(*[
|
|
259
|
+
call_function(fun, variables) for fun in itertools.chain(
|
|
294
260
|
self.description['filters'].get(level, []),
|
|
295
261
|
self.description['filters'].get(-1, []))
|
|
296
|
-
])
|
|
262
|
+
]))
|
|
297
263
|
except:
|
|
298
264
|
return True
|
|
299
265
|
|
|
@@ -345,7 +311,11 @@ class Scan():
|
|
|
345
311
|
self.description['filters'][level] = []
|
|
346
312
|
self.description['filters'][level].append(func)
|
|
347
313
|
|
|
348
|
-
def set(self,
|
|
314
|
+
def set(self,
|
|
315
|
+
name: str,
|
|
316
|
+
value,
|
|
317
|
+
depends: Iterable[str] | None = None,
|
|
318
|
+
setter: Callable | None = None):
|
|
349
319
|
try:
|
|
350
320
|
dill.dumps(value)
|
|
351
321
|
except:
|
|
@@ -354,9 +324,21 @@ class Scan():
|
|
|
354
324
|
self.add_depends(name, value.symbols())
|
|
355
325
|
self.description['functions'][name] = value
|
|
356
326
|
elif callable(value):
|
|
357
|
-
|
|
358
|
-
|
|
327
|
+
if depends:
|
|
328
|
+
self.add_depends(name, depends)
|
|
329
|
+
s = ','.join(depends)
|
|
330
|
+
self.description['functions'][f'_tmp_{name}'] = value
|
|
331
|
+
self.description['functions'][name] = eval(
|
|
332
|
+
f"lambda self, {s}: self.description['functions']['_tmp_{name}']({s})"
|
|
333
|
+
)
|
|
334
|
+
else:
|
|
335
|
+
self.add_depends(name, _get_depends(value))
|
|
336
|
+
self.description['functions'][name] = value
|
|
359
337
|
else:
|
|
338
|
+
try:
|
|
339
|
+
value = Space.fromarray(value)
|
|
340
|
+
except:
|
|
341
|
+
pass
|
|
360
342
|
self.description['consts'][name] = value
|
|
361
343
|
if setter:
|
|
362
344
|
self.description['setters'][name] = setter
|
|
@@ -376,6 +358,10 @@ class Scan():
|
|
|
376
358
|
else:
|
|
377
359
|
if level is None:
|
|
378
360
|
raise ValueError('level must be provided.')
|
|
361
|
+
try:
|
|
362
|
+
range = Space.fromarray(range)
|
|
363
|
+
except:
|
|
364
|
+
pass
|
|
379
365
|
self._add_loop_var(name, level, range)
|
|
380
366
|
if isinstance(range, Expression) or callable(range):
|
|
381
367
|
self.add_depends(name, range.symbols())
|
|
@@ -432,11 +418,15 @@ class Scan():
|
|
|
432
418
|
|
|
433
419
|
async def _update_progress(self):
|
|
434
420
|
while True:
|
|
435
|
-
task = await self.
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
421
|
+
task = await self._prm_queue.get()
|
|
422
|
+
await task
|
|
423
|
+
self._prm_queue.task_done()
|
|
424
|
+
|
|
425
|
+
async def _send_msg(self):
|
|
426
|
+
while True:
|
|
427
|
+
task = await self._msg_queue.get()
|
|
428
|
+
await task
|
|
429
|
+
self._msg_queue.task_done()
|
|
440
430
|
|
|
441
431
|
async def run(self):
|
|
442
432
|
assymbly(self.description)
|
|
@@ -452,44 +442,48 @@ class Scan():
|
|
|
452
442
|
await self._run()
|
|
453
443
|
|
|
454
444
|
async def _run(self):
|
|
455
|
-
|
|
456
|
-
self.
|
|
445
|
+
send_msg = asyncio.create_task(self._send_msg())
|
|
446
|
+
update_progress_task = asyncio.create_task(self._update_progress())
|
|
447
|
+
|
|
457
448
|
self._variables = {'self': self}
|
|
458
|
-
|
|
449
|
+
|
|
450
|
+
consts = {}
|
|
451
|
+
for k, v in self.description['consts'].items():
|
|
452
|
+
if isinstance(v, Space):
|
|
453
|
+
consts[k] = v.toarray()
|
|
454
|
+
else:
|
|
455
|
+
consts[k] = v
|
|
456
|
+
|
|
457
|
+
await update_variables(self._variables, consts,
|
|
458
|
+
self.description['setters'])
|
|
459
459
|
for level, total in self.description['total'].items():
|
|
460
460
|
if total == np.inf:
|
|
461
461
|
total = None
|
|
462
462
|
self._bar[level] = tqdm(total=total)
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
self.variables[name])
|
|
471
|
-
if inspect.isawaitable(coro):
|
|
472
|
-
await coro
|
|
463
|
+
|
|
464
|
+
updates = await call_many_functions(
|
|
465
|
+
self.description['order'].get(-1, []),
|
|
466
|
+
self.description['functions'], self.variables)
|
|
467
|
+
await update_variables(self.variables, updates,
|
|
468
|
+
self.description['setters'])
|
|
469
|
+
|
|
473
470
|
self.record = await self.create_record()
|
|
474
471
|
await self.work()
|
|
475
472
|
for level, bar in self._bar.items():
|
|
476
473
|
bar.close()
|
|
477
474
|
|
|
478
|
-
while not self._task_queue.empty():
|
|
479
|
-
evt = self._task_queue.get_nowait()
|
|
480
|
-
if isinstance(evt, asyncio.Event):
|
|
481
|
-
evt.set()
|
|
482
|
-
elif inspect.isawaitable(evt):
|
|
483
|
-
await evt
|
|
484
475
|
if self._single_step:
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
476
|
+
self.variables.update(await call_many_functions(
|
|
477
|
+
self.description['order'].get(-1, []),
|
|
478
|
+
self.description['getters'], self.variables))
|
|
479
|
+
|
|
480
|
+
self.emit(0, 0, 0, self.variables)
|
|
481
|
+
self.emit(-1, 0, 0, {})
|
|
482
|
+
|
|
483
|
+
await self._prm_queue.join()
|
|
484
|
+
update_progress_task.cancel()
|
|
485
|
+
await self._msg_queue.join()
|
|
486
|
+
send_msg.cancel()
|
|
493
487
|
return self.variables
|
|
494
488
|
|
|
495
489
|
async def done(self):
|
|
@@ -536,8 +530,8 @@ class Scan():
|
|
|
536
530
|
return
|
|
537
531
|
step = 0
|
|
538
532
|
position = 0
|
|
539
|
-
self.
|
|
540
|
-
self.
|
|
533
|
+
self._prm_queue.put_nowait(self._reset_progress_bar(
|
|
534
|
+
self.current_level))
|
|
541
535
|
async for variables in _iter_level(
|
|
542
536
|
self.variables,
|
|
543
537
|
self.description['loops'].get(self.current_level, []),
|
|
@@ -548,23 +542,18 @@ class Scan():
|
|
|
548
542
|
if await self._filter(variables, self.current_level - 1):
|
|
549
543
|
yield variables
|
|
550
544
|
self._single_step = False
|
|
551
|
-
|
|
552
|
-
self.emit(self.current_level - 1, step, position,
|
|
553
|
-
variables.copy()))
|
|
545
|
+
self.emit(self.current_level - 1, step, position, variables)
|
|
554
546
|
step += 1
|
|
555
547
|
position += 1
|
|
556
548
|
self._current_level -= 1
|
|
557
|
-
self.
|
|
549
|
+
self._prm_queue.put_nowait(
|
|
558
550
|
self._update_progress_bar(self.current_level, 1))
|
|
559
551
|
if self.current_level == 0:
|
|
560
|
-
|
|
552
|
+
self.emit(self.current_level - 1, 0, 0, {})
|
|
561
553
|
for name, value in self.variables.items():
|
|
562
554
|
if inspect.isawaitable(value):
|
|
563
555
|
self.variables[name] = await value
|
|
564
|
-
|
|
565
|
-
task = self._task_queue.get_nowait()
|
|
566
|
-
if inspect.isawaitable(task):
|
|
567
|
-
await task
|
|
556
|
+
await self._prm_queue.join()
|
|
568
557
|
|
|
569
558
|
async def work(self, **kwds):
|
|
570
559
|
if self.current_level in self.description['actions']:
|
|
@@ -589,7 +578,8 @@ class Scan():
|
|
|
589
578
|
"""
|
|
590
579
|
self.description['actions'][level] = action
|
|
591
580
|
|
|
592
|
-
async def promise(self, awaitable: Awaitable
|
|
581
|
+
async def promise(self, awaitable: Awaitable | Callable, *args,
|
|
582
|
+
**kwds) -> Promise:
|
|
593
583
|
"""
|
|
594
584
|
Promise to calculate asynchronous function and return the result in future.
|
|
595
585
|
|
|
@@ -602,8 +592,19 @@ class Scan():
|
|
|
602
592
|
if inspect.isawaitable(awaitable):
|
|
603
593
|
async with self._sem:
|
|
604
594
|
task = asyncio.create_task(self._await(awaitable))
|
|
605
|
-
self.
|
|
595
|
+
self._prm_queue.put_nowait(task)
|
|
606
596
|
return Promise(task)
|
|
597
|
+
elif inspect.iscoroutinefunction(awaitable):
|
|
598
|
+
return await self.promise(awaitable(*args, **kwds))
|
|
599
|
+
elif callable(awaitable):
|
|
600
|
+
try:
|
|
601
|
+
buf = dill.dumps((awaitable, args, kwds))
|
|
602
|
+
task = asyncio.get_running_loop().run_in_executor(
|
|
603
|
+
self._executors, _run_function_in_process, buf)
|
|
604
|
+
self._prm_queue.put_nowait(task)
|
|
605
|
+
return Promise(task)
|
|
606
|
+
except:
|
|
607
|
+
return awaitable(*args, **kwds)
|
|
607
608
|
else:
|
|
608
609
|
return awaitable
|
|
609
610
|
|
|
@@ -612,51 +613,6 @@ class Scan():
|
|
|
612
613
|
return await awaitable
|
|
613
614
|
|
|
614
615
|
|
|
615
|
-
class Unpicklable:
|
|
616
|
-
|
|
617
|
-
def __init__(self, obj):
|
|
618
|
-
self.type = str(type(obj))
|
|
619
|
-
self.id = id(obj)
|
|
620
|
-
|
|
621
|
-
def __repr__(self):
|
|
622
|
-
return f'<Unpicklable: {self.type} at 0x{id(self):x}>'
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
class TooLarge:
|
|
626
|
-
|
|
627
|
-
def __init__(self, obj):
|
|
628
|
-
self.type = str(type(obj))
|
|
629
|
-
self.id = id(obj)
|
|
630
|
-
|
|
631
|
-
def __repr__(self):
|
|
632
|
-
return f'<TooLarge: {self.type} at 0x{id(self):x}>'
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
def dump_globals(ns=None, *, size_limit=10 * 1024 * 1024, warn=False):
|
|
636
|
-
import __main__
|
|
637
|
-
|
|
638
|
-
if ns is None:
|
|
639
|
-
ns = __main__.__dict__
|
|
640
|
-
|
|
641
|
-
namespace = {}
|
|
642
|
-
|
|
643
|
-
for name, value in ns.items():
|
|
644
|
-
try:
|
|
645
|
-
buf = dill.dumps(value)
|
|
646
|
-
except:
|
|
647
|
-
namespace[name] = Unpicklable(value)
|
|
648
|
-
if warn:
|
|
649
|
-
warnings.warn(f'Unpicklable: {name} {type(value)}')
|
|
650
|
-
if len(buf) > size_limit:
|
|
651
|
-
namespace[name] = TooLarge(value)
|
|
652
|
-
if warn:
|
|
653
|
-
warnings.warn(f'TooLarge: {name} {type(value)}')
|
|
654
|
-
else:
|
|
655
|
-
namespace[name] = buf
|
|
656
|
-
|
|
657
|
-
return namespace
|
|
658
|
-
|
|
659
|
-
|
|
660
616
|
def assymbly(description):
|
|
661
617
|
import __main__
|
|
662
618
|
from IPython import get_ipython
|
|
@@ -782,16 +738,52 @@ def assymbly(description):
|
|
|
782
738
|
if ready:
|
|
783
739
|
description['order'][level].append(ready)
|
|
784
740
|
keys -= set(ready)
|
|
741
|
+
|
|
742
|
+
axis = {}
|
|
743
|
+
independent_variables = set()
|
|
744
|
+
|
|
745
|
+
for name in description['consts']:
|
|
746
|
+
axis[name] = ()
|
|
747
|
+
for level, range_list in description['loops'].items():
|
|
748
|
+
for name, iterable in range_list:
|
|
749
|
+
if isinstance(iterable, OptimizeSpace):
|
|
750
|
+
axis[name] = tuple(range(level + 1))
|
|
751
|
+
continue
|
|
752
|
+
elif isinstance(iterable, (np.ndarray, list, tuple, range, Space)):
|
|
753
|
+
independent_variables.add(name)
|
|
754
|
+
axis[name] = (level, )
|
|
755
|
+
|
|
756
|
+
for level, group in description['order'].items():
|
|
757
|
+
for names in group:
|
|
758
|
+
for name in names:
|
|
759
|
+
if name not in description['dependents']:
|
|
760
|
+
if name not in axis:
|
|
761
|
+
axis[name] = (level, )
|
|
762
|
+
else:
|
|
763
|
+
d = set()
|
|
764
|
+
for n in description['dependents'][name]:
|
|
765
|
+
d.update(axis[n])
|
|
766
|
+
if name not in axis:
|
|
767
|
+
axis[name] = tuple(sorted(d))
|
|
768
|
+
else:
|
|
769
|
+
axis[name] = tuple(sorted(set(axis[name]) | d))
|
|
770
|
+
description['axis'] = axis
|
|
771
|
+
description['independent_variables'] = independent_variables
|
|
772
|
+
|
|
785
773
|
return description
|
|
786
774
|
|
|
787
775
|
|
|
788
|
-
async def
|
|
776
|
+
async def update_variables(variables: dict[str, Any], updates: dict[str, Any],
|
|
777
|
+
setters: dict[str, Callable]):
|
|
778
|
+
coros = []
|
|
789
779
|
for name, value in updates.items():
|
|
790
780
|
if name in setters:
|
|
791
781
|
coro = setters[name](value)
|
|
792
782
|
if inspect.isawaitable(coro):
|
|
793
|
-
|
|
783
|
+
coros.append(coro)
|
|
794
784
|
variables[name] = value
|
|
785
|
+
if coros:
|
|
786
|
+
await asyncio.gather(*coros)
|
|
795
787
|
|
|
796
788
|
|
|
797
789
|
async def _iter_level(variables,
|
|
@@ -813,6 +805,8 @@ async def _iter_level(variables,
|
|
|
813
805
|
opts[iter.optimizer.name] = iter.optimizer.create()
|
|
814
806
|
elif isinstance(iter, Expression):
|
|
815
807
|
iters_d[name] = iter.eval(env)
|
|
808
|
+
elif isinstance(iter, Space):
|
|
809
|
+
iters_d[name] = iter.toarray()
|
|
816
810
|
elif callable(iter):
|
|
817
811
|
iters_d[name] = await call_function(iter, variables)
|
|
818
812
|
else:
|
|
@@ -824,31 +818,23 @@ async def _iter_level(variables,
|
|
|
824
818
|
maxiter = min(maxiter, opt_cfg.maxiter)
|
|
825
819
|
|
|
826
820
|
async for args in async_zip(*iters_d.values(), range(maxiter)):
|
|
827
|
-
await
|
|
828
|
-
|
|
821
|
+
await update_variables(variables, dict(zip(iters_d.keys(), args[:-1])),
|
|
822
|
+
setters)
|
|
829
823
|
for name, opt in opts.items():
|
|
830
824
|
args = opt.ask()
|
|
831
825
|
opt_cfg = optimizers[name]
|
|
832
|
-
await
|
|
826
|
+
await update_variables(variables, {
|
|
833
827
|
n: v
|
|
834
828
|
for n, v in zip(opt_cfg.dimensions.keys(), args)
|
|
835
829
|
}, setters)
|
|
836
830
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
await _update_variables(variables, {
|
|
841
|
-
name:
|
|
842
|
-
await call_function(functions[name], variables)
|
|
843
|
-
}, setters)
|
|
831
|
+
await update_variables(
|
|
832
|
+
variables, await call_many_functions(order, functions, variables),
|
|
833
|
+
setters)
|
|
844
834
|
|
|
845
835
|
yield variables
|
|
846
836
|
|
|
847
|
-
|
|
848
|
-
for name in group:
|
|
849
|
-
if name in getters:
|
|
850
|
-
variables[name] = await call_function(
|
|
851
|
-
getters[name], variables)
|
|
837
|
+
variables.update(await call_many_functions(order, getters, variables))
|
|
852
838
|
|
|
853
839
|
for name, opt in opts.items():
|
|
854
840
|
opt_cfg = optimizers[name]
|
|
@@ -866,10 +852,28 @@ async def _iter_level(variables,
|
|
|
866
852
|
for name, opt in opts.items():
|
|
867
853
|
opt_cfg = optimizers[name]
|
|
868
854
|
result = opt.get_result()
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
855
|
+
await update_variables(
|
|
856
|
+
variables, {
|
|
857
|
+
name: value
|
|
858
|
+
for name, value in zip(opt_cfg.dimensions.keys(), result.x)
|
|
859
|
+
}, setters)
|
|
873
860
|
variables[name] = result.fun
|
|
874
861
|
if opts:
|
|
875
862
|
yield variables
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
async def call_many_functions(order: list[list[str]],
|
|
866
|
+
functions: dict[str, Callable],
|
|
867
|
+
variables: dict[str, Any]) -> dict[str, Any]:
|
|
868
|
+
ret = {}
|
|
869
|
+
for group in order:
|
|
870
|
+
waited = []
|
|
871
|
+
coros = []
|
|
872
|
+
for name in group:
|
|
873
|
+
if name in functions:
|
|
874
|
+
waited.append(name)
|
|
875
|
+
coros.append(call_function(functions[name], variables | ret))
|
|
876
|
+
if coros:
|
|
877
|
+
results = await asyncio.gather(*coros)
|
|
878
|
+
ret.update(dict(zip(waited, results)))
|
|
879
|
+
return ret
|
qulab/scan/server.py
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import pickle
|
|
3
|
-
|
|
4
|
-
import time
|
|
5
|
-
import uuid
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from .scan import Scan
|
|
3
|
+
|
|
8
4
|
import click
|
|
9
5
|
import dill
|
|
10
|
-
import numpy as np
|
|
11
6
|
import zmq
|
|
12
7
|
from loguru import logger
|
|
13
8
|
|
|
14
9
|
from qulab.sys.rpc.zmq_socket import ZMQContextManager
|
|
15
10
|
|
|
11
|
+
from .scan import Scan
|
|
12
|
+
|
|
16
13
|
pool = {}
|
|
17
14
|
|
|
15
|
+
|
|
18
16
|
class Request():
|
|
19
17
|
__slots__ = ['sock', 'identity', 'msg', 'method']
|
|
20
18
|
|