QuLab 2.2.4__cp312-cp312-win_amd64.whl → 2.2.5__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {QuLab-2.2.4.dist-info → QuLab-2.2.5.dist-info}/METADATA +1 -1
- {QuLab-2.2.4.dist-info → QuLab-2.2.5.dist-info}/RECORD +12 -12
- qulab/fun.cp312-win_amd64.pyd +0 -0
- qulab/scan/record.py +45 -9
- qulab/scan/scan.py +19 -12
- qulab/scan/server.py +63 -13
- qulab/scan/space.py +2 -0
- qulab/version.py +1 -1
- {QuLab-2.2.4.dist-info → QuLab-2.2.5.dist-info}/LICENSE +0 -0
- {QuLab-2.2.4.dist-info → QuLab-2.2.5.dist-info}/WHEEL +0 -0
- {QuLab-2.2.4.dist-info → QuLab-2.2.5.dist-info}/entry_points.txt +0 -0
- {QuLab-2.2.4.dist-info → QuLab-2.2.5.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
qulab/__init__.py,sha256=vkFybY8YSsQilYdThPRD83-btPAR41sy_WCXiM-6mME,141
|
|
2
2
|
qulab/__main__.py,sha256=V7iokU7awstgjCeiF_hoOdFyrqJwC_4QetiLe7cWvOQ,454
|
|
3
|
-
qulab/fun.cp312-win_amd64.pyd,sha256=
|
|
4
|
-
qulab/version.py,sha256=
|
|
3
|
+
qulab/fun.cp312-win_amd64.pyd,sha256=2bsHNMUaHgDI2Hdtivzp8n6F8T-iO9HkaoTVVD6dwP0,32256
|
|
4
|
+
qulab/version.py,sha256=zyH0HuxKprjHeDzaU3CDFspw9f0RqC0qcLriAPqWD_M,21
|
|
5
5
|
qulab/monitor/__init__.py,sha256=xEVDkJF8issrsDeLqQmDsvtRmrf-UiViFcGTWuzdlFU,43
|
|
6
6
|
qulab/monitor/__main__.py,sha256=k2H1H5Zf9LLXTDLISJkbikLH-z0f1e5i5i6wXXYPOrE,105
|
|
7
7
|
qulab/monitor/config.py,sha256=y_5StMkdrbZO1ziyKBrvIkB7Jclp9RCPK1QbsOhCxnY,785
|
|
@@ -18,10 +18,10 @@ qulab/scan/expression.py,sha256=vwUM9E0OFQal4bljlUtLR3NJu4zGRyuWYrdyZSs3QTU,1619
|
|
|
18
18
|
qulab/scan/models.py,sha256=ZvXkJEt5Yz3Sjx0JKzYka-q2Uo-w_iVzHgH8A6DbjF0,18236
|
|
19
19
|
qulab/scan/optimize.py,sha256=MlT4y422CnP961IR384UKryyZh8riNvrPSd2z_MXLEg,2356
|
|
20
20
|
qulab/scan/query.py,sha256=z94Ofbii4HZ4ZyZ7nos9-y37bH8-Xg4mA3QsF_aWx-c,11907
|
|
21
|
-
qulab/scan/record.py,sha256=
|
|
22
|
-
qulab/scan/scan.py,sha256=
|
|
23
|
-
qulab/scan/server.py,sha256=
|
|
24
|
-
qulab/scan/space.py,sha256=
|
|
21
|
+
qulab/scan/record.py,sha256=crlNIm1ki6td2Z284D78M276aso7KhMnr4qjCCOj1Kk,21644
|
|
22
|
+
qulab/scan/scan.py,sha256=CNX2Z6vWMax9SqBWWYLghkz6TwIaLNnKypeyS7UCTfA,35773
|
|
23
|
+
qulab/scan/server.py,sha256=ZWTnlK_YMfPTCoq6GnGpsZzSFGyoO4qF7aEGW9wCwhM,14542
|
|
24
|
+
qulab/scan/space.py,sha256=fWsY99HDauk89f5ygqnJYBoeNFGTUkAGFssrw-YMHok,5453
|
|
25
25
|
qulab/scan/utils.py,sha256=30qnYvyFyctwcWxOCUpCNxXgGysA7xdIDzYbjwxGUzA,3744
|
|
26
26
|
qulab/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
27
|
qulab/storage/__main__.py,sha256=6-EjN0waX1yfcMPJXqpIr9UlrIEsSCFApm5G-ZeaPMQ,1742
|
|
@@ -78,9 +78,9 @@ qulab/visualization/plot_layout.py,sha256=yAnMONOms7_szCdng-8wPpUMPis5UnbaNNzV4K
|
|
|
78
78
|
qulab/visualization/plot_seq.py,sha256=h9D0Yl_yO64IwlvBgzMu9EBKr9gg6y8QE55gu2PfTns,2783
|
|
79
79
|
qulab/visualization/qdat.py,sha256=HubXFu4nfcA7iUzghJGle1C86G6221hicLR0b-GqhKQ,5887
|
|
80
80
|
qulab/visualization/widgets.py,sha256=HcYwdhDtLreJiYaZuN3LfofjJmZcLwjMfP5aasebgDo,3266
|
|
81
|
-
QuLab-2.2.
|
|
82
|
-
QuLab-2.2.
|
|
83
|
-
QuLab-2.2.
|
|
84
|
-
QuLab-2.2.
|
|
85
|
-
QuLab-2.2.
|
|
86
|
-
QuLab-2.2.
|
|
81
|
+
QuLab-2.2.5.dist-info/LICENSE,sha256=b4NRQ-GFVpJMT7RuExW3NwhfbrYsX7AcdB7Gudok-fs,1086
|
|
82
|
+
QuLab-2.2.5.dist-info/METADATA,sha256=BspoYai-nQ5IQpf_AqvzYlKLZ3QZwUfDbw9Cz_73yME,3609
|
|
83
|
+
QuLab-2.2.5.dist-info/WHEEL,sha256=fZWyj_84lK0cA-ZNCsdwhbJl0OTrpWkxInEn424qrSs,102
|
|
84
|
+
QuLab-2.2.5.dist-info/entry_points.txt,sha256=ohBzutEnQimP_BZWiuXdSliu4QAYSHHcN0PZD8c7ZCY,46
|
|
85
|
+
QuLab-2.2.5.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
|
|
86
|
+
QuLab-2.2.5.dist-info/RECORD,,
|
qulab/fun.cp312-win_amd64.pyd
CHANGED
|
Binary file
|
qulab/scan/record.py
CHANGED
|
@@ -115,7 +115,8 @@ class BufferList():
|
|
|
115
115
|
self.inner_shape = value.shape
|
|
116
116
|
elif self.inner_shape != value.shape:
|
|
117
117
|
self.inner_shape = ()
|
|
118
|
-
self.
|
|
118
|
+
with self._lock:
|
|
119
|
+
self._list.append((pos, value))
|
|
119
120
|
if len(self._list) > 1000:
|
|
120
121
|
self.flush()
|
|
121
122
|
|
|
@@ -156,14 +157,29 @@ class BufferList():
|
|
|
156
157
|
else:
|
|
157
158
|
server, record_id, key = self._data_id
|
|
158
159
|
with ZMQContextManager(zmq.DEALER, connect=server) as socket:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
160
|
+
iter_id = b''
|
|
161
|
+
start = 0
|
|
162
|
+
try:
|
|
163
|
+
while True:
|
|
164
|
+
socket.send_pyobj({
|
|
165
|
+
'method': 'bufferlist_iter',
|
|
166
|
+
'record_id': record_id,
|
|
167
|
+
'iter_id': iter_id,
|
|
168
|
+
'key': key,
|
|
169
|
+
'slice': self._slice,
|
|
170
|
+
'start': start
|
|
171
|
+
})
|
|
172
|
+
iter_id, lst, finished = socket.recv_pyobj()
|
|
173
|
+
start += len(lst)
|
|
174
|
+
yield from lst
|
|
175
|
+
if finished:
|
|
176
|
+
break
|
|
177
|
+
finally:
|
|
178
|
+
if iter_id:
|
|
179
|
+
socket.send_pyobj({
|
|
180
|
+
'method': 'bufferlist_iter_exit',
|
|
181
|
+
'iter_id': iter_id
|
|
182
|
+
})
|
|
167
183
|
|
|
168
184
|
def value(self):
|
|
169
185
|
d = []
|
|
@@ -304,6 +320,14 @@ class Record():
|
|
|
304
320
|
self._last_vars = set()
|
|
305
321
|
self._file = None
|
|
306
322
|
|
|
323
|
+
for name, value in self.description['intrinsic_loops'].items():
|
|
324
|
+
self._items[name] = value
|
|
325
|
+
|
|
326
|
+
for level, range_list in description['loops'].items():
|
|
327
|
+
for name, iterable in range_list:
|
|
328
|
+
if name in self.description['independent_variables']:
|
|
329
|
+
self._items[name] = iterable
|
|
330
|
+
|
|
307
331
|
if self.is_local_record():
|
|
308
332
|
self.database = Path(self.database)
|
|
309
333
|
self._file = random_path(self.database / 'objects')
|
|
@@ -395,6 +419,11 @@ class Record():
|
|
|
395
419
|
else:
|
|
396
420
|
ret._data_id = self.database, self.id, key
|
|
397
421
|
return ret
|
|
422
|
+
elif isinstance(ret, Space):
|
|
423
|
+
if buffer_to_array:
|
|
424
|
+
return ret.toarray()
|
|
425
|
+
else:
|
|
426
|
+
return ret
|
|
398
427
|
else:
|
|
399
428
|
return ret
|
|
400
429
|
else:
|
|
@@ -410,6 +439,11 @@ class Record():
|
|
|
410
439
|
return d.array()
|
|
411
440
|
else:
|
|
412
441
|
return d
|
|
442
|
+
elif isinstance(d, Space):
|
|
443
|
+
if buffer_to_array:
|
|
444
|
+
return d.toarray()
|
|
445
|
+
else:
|
|
446
|
+
return d
|
|
413
447
|
else:
|
|
414
448
|
return d
|
|
415
449
|
|
|
@@ -516,6 +550,8 @@ class Record():
|
|
|
516
550
|
with z.open('record.pkl', 'w') as f:
|
|
517
551
|
self.description['entry']['scripts'] = self.scripts()
|
|
518
552
|
dill.dump((self.description, items), f)
|
|
553
|
+
with z.open('config.pkl', 'w') as f:
|
|
554
|
+
f.write(dill.dumps(self.config()))
|
|
519
555
|
|
|
520
556
|
def scripts(self, session=None):
|
|
521
557
|
scripts = self.description['entry']['scripts']
|
qulab/scan/scan.py
CHANGED
|
@@ -225,6 +225,7 @@ class Scan():
|
|
|
225
225
|
dump_globals: bool = False,
|
|
226
226
|
max_workers: int = 4,
|
|
227
227
|
max_promise: int = 100,
|
|
228
|
+
max_message: int = 1000,
|
|
228
229
|
mixin=None):
|
|
229
230
|
self.id = task_uuid()
|
|
230
231
|
self.record = None
|
|
@@ -265,11 +266,12 @@ class Scan():
|
|
|
265
266
|
self._sem = asyncio.Semaphore(max_promise + 1)
|
|
266
267
|
self._bar: dict[int, tqdm] = {}
|
|
267
268
|
self._hide_pattern_re = re.compile('|'.join(self.description['hiden']))
|
|
268
|
-
self._msg_queue = asyncio.Queue()
|
|
269
|
+
self._msg_queue = asyncio.Queue(max_message)
|
|
269
270
|
self._prm_queue = asyncio.Queue()
|
|
270
271
|
self._single_step = True
|
|
271
272
|
self._max_workers = max_workers
|
|
272
273
|
self._max_promise = max_promise
|
|
274
|
+
self._max_message = max_message
|
|
273
275
|
self._executors = ProcessPoolExecutor(max_workers=max_workers)
|
|
274
276
|
|
|
275
277
|
def __del__(self):
|
|
@@ -297,7 +299,7 @@ class Scan():
|
|
|
297
299
|
self._main_task = None
|
|
298
300
|
self._bar = {}
|
|
299
301
|
self._prm_queue = asyncio.Queue()
|
|
300
|
-
self._msg_queue = asyncio.Queue()
|
|
302
|
+
self._msg_queue = asyncio.Queue(self._max_message)
|
|
301
303
|
self._sem = asyncio.Semaphore(self._max_promise + 1)
|
|
302
304
|
self._executors = ProcessPoolExecutor(max_workers=self._max_workers)
|
|
303
305
|
for opt in self.description['optimizers'].values():
|
|
@@ -344,8 +346,9 @@ class Scan():
|
|
|
344
346
|
for k, v in variables.items() if not self.hiden(k)
|
|
345
347
|
})
|
|
346
348
|
|
|
347
|
-
def emit(self, current_level, step, position, variables: dict[str,
|
|
348
|
-
|
|
349
|
+
async def emit(self, current_level, step, position, variables: dict[str,
|
|
350
|
+
Any]):
|
|
351
|
+
await self._msg_queue.put(
|
|
349
352
|
self._emit(current_level, step, position, variables.copy()))
|
|
350
353
|
|
|
351
354
|
def hide(self, name: str):
|
|
@@ -561,7 +564,7 @@ class Scan():
|
|
|
561
564
|
await self._run()
|
|
562
565
|
|
|
563
566
|
async def _run(self):
|
|
564
|
-
|
|
567
|
+
send_msg_task = asyncio.create_task(self._send_msg())
|
|
565
568
|
update_progress_task = asyncio.create_task(self._update_progress())
|
|
566
569
|
|
|
567
570
|
self._variables = {'self': self}
|
|
@@ -596,13 +599,13 @@ class Scan():
|
|
|
596
599
|
self.description['order'].get(-1, []),
|
|
597
600
|
self.description['getters'], self.variables))
|
|
598
601
|
|
|
599
|
-
self.emit(0, 0, 0, self.variables)
|
|
600
|
-
self.emit(-1, 0, 0, {})
|
|
602
|
+
await self.emit(0, 0, 0, self.variables)
|
|
603
|
+
await self.emit(-1, 0, 0, {})
|
|
601
604
|
|
|
602
605
|
await self._prm_queue.join()
|
|
603
606
|
update_progress_task.cancel()
|
|
604
607
|
await self._msg_queue.join()
|
|
605
|
-
|
|
608
|
+
send_msg_task.cancel()
|
|
606
609
|
return self.variables
|
|
607
610
|
|
|
608
611
|
async def done(self):
|
|
@@ -623,11 +626,14 @@ class Scan():
|
|
|
623
626
|
assymbly(self.description)
|
|
624
627
|
async with ZMQContextManager(zmq.DEALER, connect=server) as socket:
|
|
625
628
|
await socket.send_pyobj({
|
|
626
|
-
'method': '
|
|
629
|
+
'method': 'task_submit',
|
|
627
630
|
'description': dill.dumps(self.description)
|
|
628
631
|
})
|
|
629
632
|
self.id = await socket.recv_pyobj()
|
|
630
|
-
await socket.send_pyobj({
|
|
633
|
+
await socket.send_pyobj({
|
|
634
|
+
'method': 'task_get_record_id',
|
|
635
|
+
'id': self.id
|
|
636
|
+
})
|
|
631
637
|
record_id = await socket.recv_pyobj()
|
|
632
638
|
self.record = Record(record_id, self.description['database'],
|
|
633
639
|
self.description)
|
|
@@ -661,14 +667,15 @@ class Scan():
|
|
|
661
667
|
if await self._filter(variables, self.current_level - 1):
|
|
662
668
|
yield variables
|
|
663
669
|
self._single_step = False
|
|
664
|
-
self.emit(self.current_level - 1, step, position,
|
|
670
|
+
await self.emit(self.current_level - 1, step, position,
|
|
671
|
+
variables)
|
|
665
672
|
step += 1
|
|
666
673
|
position += 1
|
|
667
674
|
self._current_level -= 1
|
|
668
675
|
self._prm_queue.put_nowait(
|
|
669
676
|
self._update_progress_bar(self.current_level, 1))
|
|
670
677
|
if self.current_level == 0:
|
|
671
|
-
self.emit(self.current_level - 1, 0, 0, {})
|
|
678
|
+
await self.emit(self.current_level - 1, 0, 0, {})
|
|
672
679
|
for name, value in self.variables.items():
|
|
673
680
|
if inspect.isawaitable(value):
|
|
674
681
|
self.variables[name] = await value
|
qulab/scan/server.py
CHANGED
|
@@ -2,6 +2,7 @@ import asyncio
|
|
|
2
2
|
import os
|
|
3
3
|
import pickle
|
|
4
4
|
import time
|
|
5
|
+
import uuid
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
7
8
|
import click
|
|
@@ -29,7 +30,9 @@ else:
|
|
|
29
30
|
datapath = Path.home() / 'qulab' / 'data'
|
|
30
31
|
datapath.mkdir(parents=True, exist_ok=True)
|
|
31
32
|
|
|
33
|
+
namespace = uuid.uuid4()
|
|
32
34
|
record_cache = {}
|
|
35
|
+
buffer_list_cache = {}
|
|
33
36
|
CACHE_SIZE = 1024
|
|
34
37
|
|
|
35
38
|
pool = {}
|
|
@@ -44,6 +47,9 @@ class Request():
|
|
|
44
47
|
self.msg = pickle.loads(msg)
|
|
45
48
|
self.method = self.msg.get('method', '')
|
|
46
49
|
|
|
50
|
+
def __repr__(self):
|
|
51
|
+
return f"Request({self.method})"
|
|
52
|
+
|
|
47
53
|
|
|
48
54
|
async def reply(req, resp):
|
|
49
55
|
await req.sock.send_multipart([req.identity, pickle.dumps(resp)])
|
|
@@ -57,6 +63,11 @@ def clear_cache():
|
|
|
57
63
|
range(len(record_cache) - CACHE_SIZE)):
|
|
58
64
|
del record_cache[k]
|
|
59
65
|
|
|
66
|
+
for k, (t,
|
|
67
|
+
_) in zip(sorted(buffer_list_cache.items(), key=lambda x: x[1][0]),
|
|
68
|
+
range(len(buffer_list_cache) - CACHE_SIZE)):
|
|
69
|
+
del buffer_list_cache[k]
|
|
70
|
+
|
|
60
71
|
|
|
61
72
|
def flush_cache():
|
|
62
73
|
for k, (t, r) in record_cache.items():
|
|
@@ -142,12 +153,37 @@ async def handle(session: Session, request: Request, datapath: Path):
|
|
|
142
153
|
match request.method:
|
|
143
154
|
case 'ping':
|
|
144
155
|
await reply(request, 'pong')
|
|
145
|
-
case '
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
156
|
+
case 'bufferlist_iter':
|
|
157
|
+
if msg['iter_id'] in buffer_list_cache:
|
|
158
|
+
it = buffer_list_cache[msg['iter_id']][1]
|
|
159
|
+
iter_id = msg['iter_id']
|
|
160
|
+
else:
|
|
161
|
+
iter_id = uuid.uuid3(namespace, str(time.time_ns())).bytes
|
|
162
|
+
record = get_record(session, msg['record_id'], datapath)
|
|
163
|
+
bufferlist = record.get(msg['key'],
|
|
164
|
+
buffer_to_array=False,
|
|
165
|
+
slice=msg['slice'])
|
|
166
|
+
it = bufferlist.iter()
|
|
167
|
+
for _, _ in zip(range(msg['start']), it):
|
|
168
|
+
pass
|
|
169
|
+
current_time = time.time()
|
|
170
|
+
ret, end = [], False
|
|
171
|
+
while time.time() - current_time < 0.02:
|
|
172
|
+
try:
|
|
173
|
+
ret.append(next(it))
|
|
174
|
+
except StopIteration:
|
|
175
|
+
end = True
|
|
176
|
+
break
|
|
177
|
+
await reply(request, (iter_id, ret, end))
|
|
178
|
+
buffer_list_cache[iter_id] = time.time(), it
|
|
179
|
+
clear_cache()
|
|
180
|
+
case 'bufferlist_iter_exit':
|
|
181
|
+
try:
|
|
182
|
+
it = buffer_list_cache.pop(msg['iter_id'])[1]
|
|
183
|
+
it.throw(Exception)
|
|
184
|
+
except:
|
|
185
|
+
pass
|
|
186
|
+
clear_cache()
|
|
151
187
|
case 'record_create':
|
|
152
188
|
description = dill.loads(msg['description'])
|
|
153
189
|
await reply(request, record_create(session, description, datapath))
|
|
@@ -220,7 +256,7 @@ async def handle(session: Session, request: Request, datapath: Path):
|
|
|
220
256
|
'objects').parts[-4:]))
|
|
221
257
|
session.commit()
|
|
222
258
|
await reply(request, config.id)
|
|
223
|
-
case '
|
|
259
|
+
case 'task_submit':
|
|
224
260
|
from .scan import Scan
|
|
225
261
|
finished = [(id, queried) for id, (task, queried) in pool.items()
|
|
226
262
|
if not isinstance(task, int) and task.finished()]
|
|
@@ -235,7 +271,7 @@ async def handle(session: Session, request: Request, datapath: Path):
|
|
|
235
271
|
task.start()
|
|
236
272
|
pool[task.id] = [task, False]
|
|
237
273
|
await reply(request, task.id)
|
|
238
|
-
case '
|
|
274
|
+
case 'task_get_record_id':
|
|
239
275
|
task, queried = pool.get(msg['id'])
|
|
240
276
|
if isinstance(task, int):
|
|
241
277
|
await reply(request, task)
|
|
@@ -249,15 +285,28 @@ async def handle(session: Session, request: Request, datapath: Path):
|
|
|
249
285
|
await asyncio.sleep(1)
|
|
250
286
|
else:
|
|
251
287
|
await reply(request, None)
|
|
288
|
+
case 'task_get_progress':
|
|
289
|
+
task, _ = pool.get(msg['id'])
|
|
290
|
+
if isinstance(task, int):
|
|
291
|
+
await reply(request, 1)
|
|
292
|
+
else:
|
|
293
|
+
await reply(request,
|
|
294
|
+
[(bar.n, bar.total) for bar in task._bar.values()])
|
|
252
295
|
case _:
|
|
253
296
|
logger.error(f"Unknown method: {msg['method']}")
|
|
254
297
|
|
|
255
298
|
|
|
256
|
-
async def
|
|
299
|
+
async def handle_with_timeout(session: Session, request: Request,
|
|
300
|
+
datapath: Path, timeout: float):
|
|
257
301
|
try:
|
|
258
|
-
await handle(session, request, datapath)
|
|
259
|
-
|
|
260
|
-
|
|
302
|
+
await asyncio.wait_for(handle(session, request, datapath),
|
|
303
|
+
timeout=timeout)
|
|
304
|
+
except asyncio.TimeoutError:
|
|
305
|
+
logger.warning(
|
|
306
|
+
f"Task handling request {request} timed out and was cancelled.")
|
|
307
|
+
await reply(request, 'timeout')
|
|
308
|
+
except Exception as e:
|
|
309
|
+
await reply(request, f'{e!r}')
|
|
261
310
|
|
|
262
311
|
|
|
263
312
|
async def serv(port,
|
|
@@ -280,7 +329,8 @@ async def serv(port,
|
|
|
280
329
|
identity, msg = await sock.recv_multipart()
|
|
281
330
|
received += len(msg)
|
|
282
331
|
req = Request(sock, identity, msg)
|
|
283
|
-
asyncio.create_task(
|
|
332
|
+
asyncio.create_task(
|
|
333
|
+
handle_with_timeout(session, req, datapath, timeout=60.0))
|
|
284
334
|
if received > buffer_size or time.time(
|
|
285
335
|
) - last_flush_time > interval:
|
|
286
336
|
flush_cache()
|
qulab/scan/space.py
CHANGED
|
@@ -28,6 +28,8 @@ class Space():
|
|
|
28
28
|
return space
|
|
29
29
|
if isinstance(space, (list, tuple)):
|
|
30
30
|
array = np.array(space)
|
|
31
|
+
elif isinstance(space, np.ndarray):
|
|
32
|
+
array = space
|
|
31
33
|
try:
|
|
32
34
|
a = np.linspace(array[0], array[-1], len(array), dtype=array.dtype)
|
|
33
35
|
if np.allclose(a, array):
|
qulab/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.2.
|
|
1
|
+
__version__ = "2.2.5"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|