QuLab 2.0.9__tar.gz → 2.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. {qulab-2.0.9 → qulab-2.1.0}/PKG-INFO +1 -1
  2. {qulab-2.0.9 → qulab-2.1.0}/QuLab.egg-info/PKG-INFO +1 -1
  3. {qulab-2.0.9 → qulab-2.1.0}/qulab/scan/recorder.py +115 -34
  4. {qulab-2.0.9 → qulab-2.1.0}/qulab/scan/scan.py +126 -84
  5. qulab-2.1.0/qulab/version.py +1 -0
  6. qulab-2.0.9/qulab/version.py +0 -1
  7. {qulab-2.0.9 → qulab-2.1.0}/LICENSE +0 -0
  8. {qulab-2.0.9 → qulab-2.1.0}/MANIFEST.in +0 -0
  9. {qulab-2.0.9 → qulab-2.1.0}/QuLab.egg-info/SOURCES.txt +0 -0
  10. {qulab-2.0.9 → qulab-2.1.0}/QuLab.egg-info/dependency_links.txt +0 -0
  11. {qulab-2.0.9 → qulab-2.1.0}/QuLab.egg-info/entry_points.txt +0 -0
  12. {qulab-2.0.9 → qulab-2.1.0}/QuLab.egg-info/requires.txt +0 -0
  13. {qulab-2.0.9 → qulab-2.1.0}/QuLab.egg-info/top_level.txt +0 -0
  14. {qulab-2.0.9 → qulab-2.1.0}/README.md +0 -0
  15. {qulab-2.0.9 → qulab-2.1.0}/pyproject.toml +0 -0
  16. {qulab-2.0.9 → qulab-2.1.0}/qulab/__init__.py +0 -0
  17. {qulab-2.0.9 → qulab-2.1.0}/qulab/__main__.py +0 -0
  18. {qulab-2.0.9 → qulab-2.1.0}/qulab/monitor/__init__.py +0 -0
  19. {qulab-2.0.9 → qulab-2.1.0}/qulab/monitor/__main__.py +0 -0
  20. {qulab-2.0.9 → qulab-2.1.0}/qulab/monitor/config.py +0 -0
  21. {qulab-2.0.9 → qulab-2.1.0}/qulab/monitor/dataset.py +0 -0
  22. {qulab-2.0.9 → qulab-2.1.0}/qulab/monitor/event_queue.py +0 -0
  23. {qulab-2.0.9 → qulab-2.1.0}/qulab/monitor/mainwindow.py +0 -0
  24. {qulab-2.0.9 → qulab-2.1.0}/qulab/monitor/monitor.py +0 -0
  25. {qulab-2.0.9 → qulab-2.1.0}/qulab/monitor/ploter.py +0 -0
  26. {qulab-2.0.9 → qulab-2.1.0}/qulab/monitor/qt_compat.py +0 -0
  27. {qulab-2.0.9 → qulab-2.1.0}/qulab/monitor/toolbar.py +0 -0
  28. {qulab-2.0.9 → qulab-2.1.0}/qulab/scan/__init__.py +0 -0
  29. {qulab-2.0.9 → qulab-2.1.0}/qulab/scan/curd.py +0 -0
  30. {qulab-2.0.9 → qulab-2.1.0}/qulab/scan/expression.py +0 -0
  31. {qulab-2.0.9 → qulab-2.1.0}/qulab/scan/models.py +0 -0
  32. {qulab-2.0.9 → qulab-2.1.0}/qulab/scan/optimize.py +0 -0
  33. {qulab-2.0.9 → qulab-2.1.0}/qulab/scan/query_record.py +0 -0
  34. {qulab-2.0.9 → qulab-2.1.0}/qulab/scan/server.py +0 -0
  35. {qulab-2.0.9 → qulab-2.1.0}/qulab/scan/utils.py +0 -0
  36. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/__init__.py +0 -0
  37. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/__main__.py +0 -0
  38. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/backend/__init__.py +0 -0
  39. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/backend/redis.py +0 -0
  40. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/base_dataset.py +0 -0
  41. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/chunk.py +0 -0
  42. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/dataset.py +0 -0
  43. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/file.py +0 -0
  44. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/models/__init__.py +0 -0
  45. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/models/base.py +0 -0
  46. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/models/config.py +0 -0
  47. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/models/file.py +0 -0
  48. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/models/ipy.py +0 -0
  49. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/models/models.py +0 -0
  50. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/models/record.py +0 -0
  51. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/models/report.py +0 -0
  52. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/models/tag.py +0 -0
  53. {qulab-2.0.9 → qulab-2.1.0}/qulab/storage/storage.py +0 -0
  54. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/__init__.py +0 -0
  55. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/chat.py +0 -0
  56. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/device/__init__.py +0 -0
  57. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/device/basedevice.py +0 -0
  58. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/device/loader.py +0 -0
  59. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/device/utils.py +0 -0
  60. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/drivers/FakeInstrument.py +0 -0
  61. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/drivers/__init__.py +0 -0
  62. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/ipy_events.py +0 -0
  63. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/net/__init__.py +0 -0
  64. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/net/bencoder.py +0 -0
  65. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/net/cli.py +0 -0
  66. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/net/dhcp.py +0 -0
  67. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/net/dhcpd.py +0 -0
  68. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/net/kad.py +0 -0
  69. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/net/kcp.py +0 -0
  70. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/net/nginx.py +0 -0
  71. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/progress.py +0 -0
  72. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/__init__.py +0 -0
  73. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/client.py +0 -0
  74. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/exceptions.py +0 -0
  75. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/msgpack.py +0 -0
  76. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/msgpack.pyi +0 -0
  77. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/rpc.py +0 -0
  78. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/serialize.py +0 -0
  79. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/server.py +0 -0
  80. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/socket.py +0 -0
  81. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/utils.py +0 -0
  82. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/worker.py +0 -0
  83. {qulab-2.0.9 → qulab-2.1.0}/qulab/sys/rpc/zmq_socket.py +0 -0
  84. {qulab-2.0.9 → qulab-2.1.0}/qulab/visualization/__init__.py +0 -0
  85. {qulab-2.0.9 → qulab-2.1.0}/qulab/visualization/__main__.py +0 -0
  86. {qulab-2.0.9 → qulab-2.1.0}/qulab/visualization/_autoplot.py +0 -0
  87. {qulab-2.0.9 → qulab-2.1.0}/qulab/visualization/plot_layout.py +0 -0
  88. {qulab-2.0.9 → qulab-2.1.0}/qulab/visualization/plot_seq.py +0 -0
  89. {qulab-2.0.9 → qulab-2.1.0}/qulab/visualization/qdat.py +0 -0
  90. {qulab-2.0.9 → qulab-2.1.0}/qulab/visualization/widgets.py +0 -0
  91. {qulab-2.0.9 → qulab-2.1.0}/setup.cfg +0 -0
  92. {qulab-2.0.9 → qulab-2.1.0}/setup.py +0 -0
  93. {qulab-2.0.9 → qulab-2.1.0}/src/qulab.h +0 -0
  94. {qulab-2.0.9 → qulab-2.1.0}/tests/test_scan.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: QuLab
3
- Version: 2.0.9
3
+ Version: 2.1.0
4
4
  Summary: contral instruments and manage data
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: QuLab
3
- Version: 2.0.9
3
+ Version: 2.1.0
4
4
  Summary: contral instruments and manage data
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
@@ -69,14 +69,14 @@ class BufferList():
69
69
  self._list = []
70
70
  self.lu = ()
71
71
  self.rd = ()
72
- self.inner_shape = None
72
+ self.inner_shape = ()
73
73
  self.file = file
74
74
  self._slice = slice
75
75
  self._lock = Lock()
76
- self._database = None
76
+ self._data_id = None
77
77
 
78
78
  def __repr__(self):
79
- return f"<BufferList: lu={self.lu}, rd={self.rd}, slice={self._slice}>"
79
+ return f"<BufferList: shape={self.shape}, lu={self.lu}, rd={self.rd}, slice={self._slice}>"
80
80
 
81
81
  def __getstate__(self):
82
82
  self.flush()
@@ -99,11 +99,12 @@ class BufferList():
99
99
  self._list = []
100
100
  self._slice = None
101
101
  self._lock = Lock()
102
- self._database = None
102
+ self._data_id = None
103
103
 
104
104
  @property
105
105
  def shape(self):
106
- return tuple([i - j for i, j in zip(self.rd, self.lu)])
106
+ return tuple([i - j
107
+ for i, j in zip(self.rd, self.lu)]) + self.inner_shape
107
108
 
108
109
  def flush(self):
109
110
  if not self._list:
@@ -143,11 +144,27 @@ class BufferList():
143
144
  break
144
145
 
145
146
  def iter(self):
146
- for pos, value in itertools.chain(self._iter_file(), self._list):
147
- if not self._slice:
148
- yield pos, value
149
- elif all([index_in_slice(s, i) for s, i in zip(self._slice, pos)]):
150
- yield pos, value[self._slice[len(pos):]]
147
+ if self._data_id is None:
148
+ for pos, value in itertools.chain(self._iter_file(), self._list):
149
+ if not self._slice:
150
+ yield pos, value
151
+ elif all(
152
+ [index_in_slice(s, i) for s, i in zip(self._slice, pos)]):
153
+ if self.inner_shape:
154
+ yield pos, value[self._slice[len(pos):]]
155
+ else:
156
+ yield pos, value
157
+ else:
158
+ server, record_id, key = self._data_id
159
+ with ZMQContextManager(zmq.DEALER, connect=server) as socket:
160
+ socket.send_pyobj({
161
+ 'method': 'bufferlist_slice',
162
+ 'record_id': record_id,
163
+ 'key': key,
164
+ 'slice': self._slice
165
+ })
166
+ ret = socket.recv_pyobj()
167
+ yield from ret
151
168
 
152
169
  def value(self):
153
170
  d = []
@@ -170,47 +187,111 @@ class BufferList():
170
187
 
171
188
  def array(self):
172
189
  pos, data = self.items()
173
- pos = np.asarray(pos) - np.asarray(self.lu)
190
+ if self._slice:
191
+ pos = np.asarray(pos)
192
+ lu = tuple(np.min(pos, axis=0))
193
+ rd = tuple(np.max(pos, axis=0) + 1)
194
+ pos = np.asarray(pos) - np.asarray(lu)
195
+ shape = []
196
+ for k, (s, i, j) in enumerate(zip(self._slice, rd, lu)):
197
+ if s.step is not None:
198
+ pos[:, k] = pos[:, k] / s.step
199
+ shape.append(round(np.ceil((i - j) / s.step)))
200
+ else:
201
+ shape.append(i - j)
202
+ shape = tuple(shape)
203
+ else:
204
+ shape = tuple([i - j for i, j in zip(self.rd, self.lu)])
205
+ pos = np.asarray(pos) - np.asarray(self.lu)
174
206
  data = np.asarray(data)
175
207
  inner_shape = data.shape[1:]
176
- x = np.full(self.shape + inner_shape, np.nan, dtype=data[0].dtype)
208
+ x = np.full(shape + inner_shape, np.nan, dtype=data[0].dtype)
177
209
  x.__setitem__(tuple(pos.T), data)
178
210
  return x
179
211
 
180
212
  def _full_slice(self, slice_tuple: slice
181
213
  | tuple[slice | int | EllipsisType, ...]):
214
+ ndim = len(self.lu)
215
+ if self.inner_shape:
216
+ ndim += len(self.inner_shape)
217
+
182
218
  if isinstance(slice_tuple, slice):
183
- slice_tuple = (slice_tuple, ) + (slice(0, sys.maxsize,
184
- 1), ) * (len(self.lu) - 1)
219
+ slice_tuple = (
220
+ slice_tuple, ) + (slice(0, sys.maxsize, 1), ) * (ndim - 1)
185
221
  if slice_tuple is Ellipsis:
186
- slice_tuple = (slice(0, sys.maxsize, 1), ) * len(self.lu)
222
+ slice_tuple = (slice(0, sys.maxsize, 1), ) * ndim
187
223
  else:
188
- head, tail = [], []
224
+ head, tail = (), ()
189
225
  for i, s in enumerate(slice_tuple):
190
226
  if s is Ellipsis:
191
227
  head = slice_tuple[:i]
192
228
  tail = slice_tuple[i + 1:]
193
229
  break
194
- slice_tuple = head + (slice(0, sys.maxsize, 1), ) * (
195
- len(self.lu) - len(head) - len(tail)) + tail
230
+ else:
231
+ head = slice_tuple
232
+ tail = ()
233
+ slice_tuple = head + (slice(
234
+ 0, sys.maxsize, 1), ) * (ndim - len(head) - len(tail)) + tail
196
235
  slice_list = []
197
- for s in slice_tuple:
236
+ contract = []
237
+ reversed = []
238
+ for i, s in enumerate(slice_tuple):
198
239
  if isinstance(s, int):
199
- slice_list.append(s)
240
+ if s >= 0:
241
+ slice_list.append(slice(s, s + 1, 1))
242
+ elif i < len(self.lu):
243
+ s = self.rd[i] + s
244
+ slice_list.append(slice(s, s + 1, 1))
245
+ else:
246
+ slice_list.append(slice(s, s - 1, -1))
247
+ contract.append(i)
200
248
  else:
201
249
  start, stop, step = s.start, s.stop, s.step
250
+ if step is None:
251
+ step = 1
252
+ if step < 0 and i < len(self.lu):
253
+ step = -step
254
+ reversed.append(i)
255
+ if start is None and stop is None:
256
+ start, stop = 0, sys.maxsize
257
+ elif start is None:
258
+ start, stop = self.lu[i], sys.maxsize
259
+ elif stop is None:
260
+ start, stop = 0, start + self.lu[i]
261
+ else:
262
+ start, stop = stop + self.lu[i] + 1, start + self.lu[
263
+ i] + 1
264
+
202
265
  if start is None:
203
266
  start = 0
267
+ elif start < 0 and i < len(self.lu):
268
+ start = self.rd[i] + start
204
269
  if step is None:
205
270
  step = 1
206
271
  if stop is None:
207
272
  stop = sys.maxsize
273
+ elif stop < 0 and i < len(self.lu):
274
+ stop = self.rd[i] + stop
275
+
208
276
  slice_list.append(slice(start, stop, step))
209
- return tuple(slice_list)
277
+ return tuple(slice_list), contract, reversed
210
278
 
211
279
  def __getitem__(self, slice_tuple: slice | EllipsisType
212
280
  | tuple[slice | int | EllipsisType, ...]):
213
- return super().__getitem__(self._full_slice(slice_tuple))
281
+ self._slice, contract, reversed = self._full_slice(slice_tuple)
282
+ ret = self.array()
283
+ slices = []
284
+ for i, s in enumerate(self._slice):
285
+ if i in contract:
286
+ slices.append(0)
287
+ elif isinstance(s, slice):
288
+ if i in reversed:
289
+ slices.append(slice(None, None, -1))
290
+ else:
291
+ slices.append(slice(None, None, 1))
292
+ ret = ret.__getitem__(tuple(slices))
293
+ self._slice = None
294
+ return ret
214
295
 
215
296
 
216
297
  class Record():
@@ -310,9 +391,9 @@ class Record():
310
391
  self.flush()
311
392
 
312
393
  def __getitem__(self, key):
313
- return self.get(key)
394
+ return self.get(key, buffer_to_array=True)
314
395
 
315
- def get(self, key, default=_notgiven, buffer_to_array=True, slice=None):
396
+ def get(self, key, default=_notgiven, buffer_to_array=False, slice=None):
316
397
  if self.is_remote_record():
317
398
  with ZMQContextManager(zmq.DEALER,
318
399
  connect=self.database) as socket:
@@ -323,19 +404,19 @@ class Record():
323
404
  })
324
405
  ret = socket.recv_pyobj()
325
406
  if isinstance(ret, BufferList):
326
- socket.send_pyobj({
327
- 'method': 'bufferlist_slice',
328
- 'record_id': self.id,
329
- 'key': key,
330
- 'slice': slice
331
- })
332
- lst = socket.recv_pyobj()
333
- ret._list = lst
334
- ret._slice = slice
335
407
  if buffer_to_array:
408
+ socket.send_pyobj({
409
+ 'method': 'bufferlist_slice',
410
+ 'record_id': self.id,
411
+ 'key': key,
412
+ 'slice': slice
413
+ })
414
+ lst = socket.recv_pyobj()
415
+ ret._list = lst
416
+ ret._slice = slice
336
417
  return ret.array()
337
418
  else:
338
- ret._database = self.database
419
+ ret._data_id = self.database, self.id, key
339
420
  return ret
340
421
  else:
341
422
  return ret
@@ -8,6 +8,7 @@ import re
8
8
  import sys
9
9
  import uuid
10
10
  import warnings
11
+ from concurrent.futures import ProcessPoolExecutor
11
12
  from graphlib import TopologicalSorter
12
13
  from pathlib import Path
13
14
  from types import MethodType
@@ -157,6 +158,11 @@ class Promise():
157
158
  return Promise(self.task, None, attr)
158
159
 
159
160
 
161
+ def _run_function_in_process(buf):
162
+ func, args, kwds = dill.loads(buf)
163
+ return func(*args, **kwds)
164
+
165
+
160
166
  class Scan():
161
167
 
162
168
  def __new__(cls, *args, mixin=None, **kwds):
@@ -176,6 +182,8 @@ class Scan():
176
182
  database: str | Path
177
183
  | None = f'tcp://127.0.0.1:{default_record_port}',
178
184
  dump_globals: bool = False,
185
+ max_workers: int = 4,
186
+ max_promise: int = 100,
179
187
  mixin=None):
180
188
  self.id = task_uuid()
181
189
  self.record = None
@@ -207,23 +215,21 @@ class Scan():
207
215
  self._variables = {}
208
216
  self._main_task = None
209
217
  self._sock = None
210
- self._sem = asyncio.Semaphore(100)
218
+ self._sem = asyncio.Semaphore(max_promise + 1)
211
219
  self._bar: dict[int, tqdm] = {}
212
220
  self._hide_pattern_re = re.compile('|'.join(self.description['hiden']))
213
- self._task_queue = asyncio.Queue()
214
- self._task_pool = []
221
+ self._msg_queue = asyncio.Queue()
222
+ self._prm_queue = asyncio.Queue()
215
223
  self._single_step = True
224
+ self._max_workers = max_workers
225
+ self._max_promise = max_promise
226
+ self._executors = ProcessPoolExecutor(max_workers=max_workers)
216
227
 
217
228
  def __del__(self):
218
229
  try:
219
230
  self._main_task.cancel()
220
231
  except:
221
232
  pass
222
- for task in self._task_pool:
223
- try:
224
- task.cancel()
225
- except:
226
- pass
227
233
 
228
234
  def __getstate__(self) -> dict:
229
235
  state = self.__dict__.copy()
@@ -231,9 +237,10 @@ class Scan():
231
237
  del state['_sock']
232
238
  del state['_main_task']
233
239
  del state['_bar']
234
- del state['_task_queue']
235
- del state['_task_pool']
240
+ del state['_msg_queue']
241
+ del state['_prm_queue']
236
242
  del state['_sem']
243
+ del state['_executors']
237
244
  return state
238
245
 
239
246
  def __setstate__(self, state: dict) -> None:
@@ -242,12 +249,20 @@ class Scan():
242
249
  self._sock = None
243
250
  self._main_task = None
244
251
  self._bar = {}
245
- self._task_queue = asyncio.Queue()
246
- self._task_pool = []
247
- self._sem = asyncio.Semaphore(100)
252
+ self._prm_queue = asyncio.Queue()
253
+ self._msg_queue = asyncio.Queue()
254
+ self._sem = asyncio.Semaphore(self._max_promise + 1)
255
+ self._executors = ProcessPoolExecutor(max_workers=self._max_workers)
248
256
  for opt in self.description['optimizers'].values():
249
257
  opt.scanner = self
250
258
 
259
+ def __del__(self):
260
+ try:
261
+ self._main_task.cancel()
262
+ except:
263
+ pass
264
+ self._executors.shutdown()
265
+
251
266
  @property
252
267
  def current_level(self):
253
268
  return self._current_level
@@ -256,8 +271,8 @@ class Scan():
256
271
  def variables(self) -> dict[str, Any]:
257
272
  return self._variables
258
273
 
259
- async def emit(self, current_level, step, position, variables: dict[str,
260
- Any]):
274
+ async def _emit(self, current_level, step, position, variables: dict[str,
275
+ Any]):
261
276
  for key, value in list(variables.items()):
262
277
  if inspect.isawaitable(value) and not self.hiden(key):
263
278
  variables[key] = await value
@@ -280,6 +295,11 @@ class Scan():
280
295
  for k, v in variables.items() if not self.hiden(k)
281
296
  })
282
297
 
298
+ def emit(self, current_level, step, position, variables: dict[str, Any]):
299
+ self._msg_queue.put_nowait(
300
+ asyncio.create_task(
301
+ self._emit(current_level, step, position, variables.copy())))
302
+
283
303
  def hide(self, name: str):
284
304
  self.description['hiden'].append(name)
285
305
  self._hide_pattern_re = re.compile('|'.join(self.description['hiden']))
@@ -289,11 +309,11 @@ class Scan():
289
309
 
290
310
  async def _filter(self, variables: dict[str, Any], level: int = 0):
291
311
  try:
292
- return all([
293
- await call_function(fun, variables) for fun in itertools.chain(
312
+ return all(await asyncio.gather(*[
313
+ call_function(fun, variables) for fun in itertools.chain(
294
314
  self.description['filters'].get(level, []),
295
315
  self.description['filters'].get(-1, []))
296
- ])
316
+ ]))
297
317
  except:
298
318
  return True
299
319
 
@@ -432,11 +452,15 @@ class Scan():
432
452
 
433
453
  async def _update_progress(self):
434
454
  while True:
435
- task = await self._task_queue.get()
436
- if isinstance(task, asyncio.Event):
437
- task.set()
438
- elif inspect.isawaitable(task):
439
- await task
455
+ task = await self._prm_queue.get()
456
+ await task
457
+ self._prm_queue.task_done()
458
+
459
+ async def _send_msg(self):
460
+ while True:
461
+ task = await self._msg_queue.get()
462
+ await task
463
+ self._msg_queue.task_done()
440
464
 
441
465
  async def run(self):
442
466
  assymbly(self.description)
@@ -452,44 +476,41 @@ class Scan():
452
476
  await self._run()
453
477
 
454
478
  async def _run(self):
455
- task = asyncio.create_task(self._update_progress())
456
- self._task_pool.append(task)
479
+ send_msg = asyncio.create_task(self._send_msg())
480
+ update_progress_task = asyncio.create_task(self._update_progress())
481
+
457
482
  self._variables = {'self': self}
458
- self._variables.update(self.description['consts'].copy())
483
+
484
+ await update_variables(self._variables, self.description['consts'],
485
+ self.description['setters'])
459
486
  for level, total in self.description['total'].items():
460
487
  if total == np.inf:
461
488
  total = None
462
489
  self._bar[level] = tqdm(total=total)
463
- for group in self.description['order'].get(-1, []):
464
- for name in group:
465
- if name in self.description['functions']:
466
- self.variables[name] = await call_function(
467
- self.description['functions'][name], self.variables)
468
- if name in self.description['setters']:
469
- coro = self.description['setters'][name](
470
- self.variables[name])
471
- if inspect.isawaitable(coro):
472
- await coro
490
+
491
+ updates = await call_many_functions(
492
+ self.description['order'].get(-1, []),
493
+ self.description['functions'], self.variables)
494
+ await update_variables(self.variables, updates,
495
+ self.description['setters'])
496
+
473
497
  self.record = await self.create_record()
474
498
  await self.work()
475
499
  for level, bar in self._bar.items():
476
500
  bar.close()
477
501
 
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
502
  if self._single_step:
485
- for group in self.description['order'].get(-1, []):
486
- for name in group:
487
- if name in self.description['getters']:
488
- self.variables[name] = await call_function(
489
- self.description['getters'][name], self.variables)
490
- await self.emit(0, 0, 0, self.variables.copy())
491
- await self.emit(-1, 0, 0, {})
492
- task.cancel()
503
+ self.variables.update(await call_many_functions(
504
+ self.description['order'].get(-1, []),
505
+ self.description['getters'], self.variables))
506
+
507
+ self.emit(0, 0, 0, self.variables)
508
+ self.emit(-1, 0, 0, {})
509
+
510
+ await self._prm_queue.join()
511
+ update_progress_task.cancel()
512
+ await self._msg_queue.join()
513
+ send_msg.cancel()
493
514
  return self.variables
494
515
 
495
516
  async def done(self):
@@ -536,8 +557,8 @@ class Scan():
536
557
  return
537
558
  step = 0
538
559
  position = 0
539
- self._task_queue.put_nowait(
540
- self._reset_progress_bar(self.current_level))
560
+ self._prm_queue.put_nowait(self._reset_progress_bar(
561
+ self.current_level))
541
562
  async for variables in _iter_level(
542
563
  self.variables,
543
564
  self.description['loops'].get(self.current_level, []),
@@ -548,23 +569,18 @@ class Scan():
548
569
  if await self._filter(variables, self.current_level - 1):
549
570
  yield variables
550
571
  self._single_step = False
551
- asyncio.create_task(
552
- self.emit(self.current_level - 1, step, position,
553
- variables.copy()))
572
+ self.emit(self.current_level - 1, step, position, variables)
554
573
  step += 1
555
574
  position += 1
556
575
  self._current_level -= 1
557
- self._task_queue.put_nowait(
576
+ self._prm_queue.put_nowait(
558
577
  self._update_progress_bar(self.current_level, 1))
559
578
  if self.current_level == 0:
560
- await self.emit(self.current_level - 1, 0, 0, {})
579
+ self.emit(self.current_level - 1, 0, 0, {})
561
580
  for name, value in self.variables.items():
562
581
  if inspect.isawaitable(value):
563
582
  self.variables[name] = await value
564
- while not self._task_queue.empty():
565
- task = self._task_queue.get_nowait()
566
- if inspect.isawaitable(task):
567
- await task
583
+ await self._prm_queue.join()
568
584
 
569
585
  async def work(self, **kwds):
570
586
  if self.current_level in self.description['actions']:
@@ -589,7 +605,8 @@ class Scan():
589
605
  """
590
606
  self.description['actions'][level] = action
591
607
 
592
- async def promise(self, awaitable: Awaitable) -> Promise:
608
+ async def promise(self, awaitable: Awaitable | Callable, *args,
609
+ **kwds) -> Promise:
593
610
  """
594
611
  Promise to calculate asynchronous function and return the result in future.
595
612
 
@@ -602,8 +619,19 @@ class Scan():
602
619
  if inspect.isawaitable(awaitable):
603
620
  async with self._sem:
604
621
  task = asyncio.create_task(self._await(awaitable))
605
- self._task_queue.put_nowait(task)
622
+ self._prm_queue.put_nowait(task)
606
623
  return Promise(task)
624
+ elif inspect.iscoroutinefunction(awaitable):
625
+ return await self.promise(awaitable(*args, **kwds))
626
+ elif callable(awaitable):
627
+ try:
628
+ buf = dill.dumps((awaitable, args, kwds))
629
+ task = asyncio.get_running_loop().run_in_executor(
630
+ self._executors, _run_function_in_process, buf)
631
+ self._prm_queue.put_nowait(task)
632
+ return Promise(task)
633
+ except:
634
+ return awaitable(*args, **kwds)
607
635
  else:
608
636
  return awaitable
609
637
 
@@ -785,13 +813,17 @@ def assymbly(description):
785
813
  return description
786
814
 
787
815
 
788
- async def _update_variables(variables, updates, setters):
816
+ async def update_variables(variables: dict[str, Any], updates: dict[str, Any],
817
+ setters: dict[str, Callable]):
818
+ coros = []
789
819
  for name, value in updates.items():
790
820
  if name in setters:
791
821
  coro = setters[name](value)
792
822
  if inspect.isawaitable(coro):
793
- await coro
823
+ coros.append(coro)
794
824
  variables[name] = value
825
+ if coros:
826
+ await asyncio.gather(*coros)
795
827
 
796
828
 
797
829
  async def _iter_level(variables,
@@ -824,31 +856,23 @@ async def _iter_level(variables,
824
856
  maxiter = min(maxiter, opt_cfg.maxiter)
825
857
 
826
858
  async for args in async_zip(*iters_d.values(), range(maxiter)):
827
- await _update_variables(variables, dict(zip(iters_d.keys(),
828
- args[:-1])), setters)
859
+ await update_variables(variables, dict(zip(iters_d.keys(), args[:-1])),
860
+ setters)
829
861
  for name, opt in opts.items():
830
862
  args = opt.ask()
831
863
  opt_cfg = optimizers[name]
832
- await _update_variables(variables, {
864
+ await update_variables(variables, {
833
865
  n: v
834
866
  for n, v in zip(opt_cfg.dimensions.keys(), args)
835
867
  }, setters)
836
868
 
837
- for group in order:
838
- for name in group:
839
- if name in functions:
840
- await _update_variables(variables, {
841
- name:
842
- await call_function(functions[name], variables)
843
- }, setters)
869
+ await update_variables(
870
+ variables, await call_many_functions(order, functions, variables),
871
+ setters)
844
872
 
845
873
  yield variables
846
874
 
847
- for group in order:
848
- for name in group:
849
- if name in getters:
850
- variables[name] = await call_function(
851
- getters[name], variables)
875
+ variables.update(await call_many_functions(order, getters, variables))
852
876
 
853
877
  for name, opt in opts.items():
854
878
  opt_cfg = optimizers[name]
@@ -866,10 +890,28 @@ async def _iter_level(variables,
866
890
  for name, opt in opts.items():
867
891
  opt_cfg = optimizers[name]
868
892
  result = opt.get_result()
869
- variables.update({
870
- n: v
871
- for n, v in zip(opt_cfg.dimensions.keys(), result.x)
872
- })
893
+ await update_variables(
894
+ variables, {
895
+ name: value
896
+ for name, value in zip(opt_cfg.dimensions.keys(), result.x)
897
+ }, setters)
873
898
  variables[name] = result.fun
874
899
  if opts:
875
900
  yield variables
901
+
902
+
903
+ async def call_many_functions(order: list[list[str]],
904
+ functions: dict[str, Callable],
905
+ variables: dict[str, Any]) -> dict[str, Any]:
906
+ ret = {}
907
+ for group in order:
908
+ waited = []
909
+ coros = []
910
+ for name in group:
911
+ if name in functions:
912
+ waited.append(name)
913
+ coros.append(call_function(functions[name], variables | ret))
914
+ if coros:
915
+ results = await asyncio.gather(*coros)
916
+ ret.update(dict(zip(waited, results)))
917
+ return ret
@@ -0,0 +1 @@
1
+ __version__ = "2.1.0"
@@ -1 +0,0 @@
1
- __version__ = "2.0.9"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes