QuLab 2.1.0__cp312-cp312-macosx_10_9_universal2.whl → 2.1.1__cp312-cp312-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.1.0.dist-info → QuLab-2.1.1.dist-info}/METADATA +1 -1
- {QuLab-2.1.0.dist-info → QuLab-2.1.1.dist-info}/RECORD +16 -14
- qulab/fun.cpython-312-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 -491
- qulab/scan/scan.py +94 -132
- 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.1.0.dist-info → QuLab-2.1.1.dist-info}/LICENSE +0 -0
- {QuLab-2.1.0.dist-info → QuLab-2.1.1.dist-info}/WHEEL +0 -0
- {QuLab-2.1.0.dist-info → QuLab-2.1.1.dist-info}/entry_points.txt +0 -0
- {QuLab-2.1.0.dist-info → QuLab-2.1.1.dist-info}/top_level.txt +0 -0
qulab/scan/scan.py
CHANGED
|
@@ -1,35 +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 warnings
|
|
11
9
|
from concurrent.futures import ProcessPoolExecutor
|
|
12
10
|
from graphlib import TopologicalSorter
|
|
13
11
|
from pathlib import Path
|
|
14
|
-
from
|
|
15
|
-
from typing import Any, Awaitable, Callable, Iterable, Type
|
|
12
|
+
from typing import Any, Awaitable, Callable, Iterable
|
|
16
13
|
|
|
17
14
|
import dill
|
|
18
15
|
import numpy as np
|
|
19
|
-
import skopt
|
|
20
16
|
import zmq
|
|
21
|
-
from skopt.space import Categorical, Integer, Real
|
|
22
|
-
from tqdm.notebook import tqdm
|
|
23
17
|
|
|
24
18
|
from ..sys.rpc.zmq_socket import ZMQContextManager
|
|
25
19
|
from .expression import Env, Expression, Symbol
|
|
26
20
|
from .optimize import NgOptimizer
|
|
27
|
-
from .
|
|
28
|
-
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
|
+
|
|
29
41
|
|
|
30
42
|
__process_uuid = uuid.uuid1()
|
|
31
43
|
__task_counter = itertools.count()
|
|
32
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
|
+
|
|
33
50
|
|
|
34
51
|
def task_uuid():
|
|
35
52
|
return uuid.uuid3(__process_uuid, str(next(__task_counter)))
|
|
@@ -55,79 +72,6 @@ def _get_depends(func: Callable):
|
|
|
55
72
|
return args
|
|
56
73
|
|
|
57
74
|
|
|
58
|
-
class OptimizeSpace():
|
|
59
|
-
|
|
60
|
-
def __init__(self, optimizer: 'Optimizer', space):
|
|
61
|
-
self.optimizer = optimizer
|
|
62
|
-
self.space = space
|
|
63
|
-
self.name = None
|
|
64
|
-
|
|
65
|
-
def __len__(self):
|
|
66
|
-
return self.optimizer.maxiter
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
class Optimizer():
|
|
70
|
-
|
|
71
|
-
def __init__(self,
|
|
72
|
-
scanner: 'Scan',
|
|
73
|
-
name: str,
|
|
74
|
-
level: int,
|
|
75
|
-
method: str | Type = skopt.Optimizer,
|
|
76
|
-
maxiter: int = 1000,
|
|
77
|
-
minimize: bool = True,
|
|
78
|
-
**kwds):
|
|
79
|
-
self.scanner = scanner
|
|
80
|
-
self.method = method
|
|
81
|
-
self.maxiter = maxiter
|
|
82
|
-
self.dimensions = {}
|
|
83
|
-
self.name = name
|
|
84
|
-
self.level = level
|
|
85
|
-
self.kwds = kwds
|
|
86
|
-
self.minimize = minimize
|
|
87
|
-
|
|
88
|
-
def create(self):
|
|
89
|
-
return self.method(list(self.dimensions.values()), **self.kwds)
|
|
90
|
-
|
|
91
|
-
def Categorical(self,
|
|
92
|
-
categories,
|
|
93
|
-
prior=None,
|
|
94
|
-
transform=None,
|
|
95
|
-
name=None) -> OptimizeSpace:
|
|
96
|
-
return OptimizeSpace(self,
|
|
97
|
-
Categorical(categories, prior, transform, name))
|
|
98
|
-
|
|
99
|
-
def Integer(self,
|
|
100
|
-
low,
|
|
101
|
-
high,
|
|
102
|
-
prior="uniform",
|
|
103
|
-
base=10,
|
|
104
|
-
transform=None,
|
|
105
|
-
name=None,
|
|
106
|
-
dtype=np.int64) -> OptimizeSpace:
|
|
107
|
-
return OptimizeSpace(
|
|
108
|
-
self, Integer(low, high, prior, base, transform, name, dtype))
|
|
109
|
-
|
|
110
|
-
def Real(self,
|
|
111
|
-
low,
|
|
112
|
-
high,
|
|
113
|
-
prior="uniform",
|
|
114
|
-
base=10,
|
|
115
|
-
transform=None,
|
|
116
|
-
name=None,
|
|
117
|
-
dtype=float) -> OptimizeSpace:
|
|
118
|
-
return OptimizeSpace(
|
|
119
|
-
self, Real(low, high, prior, base, transform, name, dtype))
|
|
120
|
-
|
|
121
|
-
def __getstate__(self) -> dict:
|
|
122
|
-
state = self.__dict__.copy()
|
|
123
|
-
del state['scanner']
|
|
124
|
-
return state
|
|
125
|
-
|
|
126
|
-
def __setstate__(self, state: dict) -> None:
|
|
127
|
-
self.__dict__.update(state)
|
|
128
|
-
self.scanner = None
|
|
129
|
-
|
|
130
|
-
|
|
131
75
|
class Promise():
|
|
132
76
|
__slots__ = ['task', 'key', 'attr']
|
|
133
77
|
|
|
@@ -180,7 +124,7 @@ class Scan():
|
|
|
180
124
|
app: str = 'task',
|
|
181
125
|
tags: tuple[str] = (),
|
|
182
126
|
database: str | Path
|
|
183
|
-
| None =
|
|
127
|
+
| None = default_server,
|
|
184
128
|
dump_globals: bool = False,
|
|
185
129
|
max_workers: int = 4,
|
|
186
130
|
max_promise: int = 100,
|
|
@@ -201,6 +145,8 @@ class Scan():
|
|
|
201
145
|
'actions': {},
|
|
202
146
|
'dependents': {},
|
|
203
147
|
'order': {},
|
|
148
|
+
'axis': {},
|
|
149
|
+
'independent_variables': set(),
|
|
204
150
|
'filters': {},
|
|
205
151
|
'total': {},
|
|
206
152
|
'database': database,
|
|
@@ -365,7 +311,11 @@ class Scan():
|
|
|
365
311
|
self.description['filters'][level] = []
|
|
366
312
|
self.description['filters'][level].append(func)
|
|
367
313
|
|
|
368
|
-
def set(self,
|
|
314
|
+
def set(self,
|
|
315
|
+
name: str,
|
|
316
|
+
value,
|
|
317
|
+
depends: Iterable[str] | None = None,
|
|
318
|
+
setter: Callable | None = None):
|
|
369
319
|
try:
|
|
370
320
|
dill.dumps(value)
|
|
371
321
|
except:
|
|
@@ -374,9 +324,21 @@ class Scan():
|
|
|
374
324
|
self.add_depends(name, value.symbols())
|
|
375
325
|
self.description['functions'][name] = value
|
|
376
326
|
elif callable(value):
|
|
377
|
-
|
|
378
|
-
|
|
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
|
|
379
337
|
else:
|
|
338
|
+
try:
|
|
339
|
+
value = Space.fromarray(value)
|
|
340
|
+
except:
|
|
341
|
+
pass
|
|
380
342
|
self.description['consts'][name] = value
|
|
381
343
|
if setter:
|
|
382
344
|
self.description['setters'][name] = setter
|
|
@@ -396,6 +358,10 @@ class Scan():
|
|
|
396
358
|
else:
|
|
397
359
|
if level is None:
|
|
398
360
|
raise ValueError('level must be provided.')
|
|
361
|
+
try:
|
|
362
|
+
range = Space.fromarray(range)
|
|
363
|
+
except:
|
|
364
|
+
pass
|
|
399
365
|
self._add_loop_var(name, level, range)
|
|
400
366
|
if isinstance(range, Expression) or callable(range):
|
|
401
367
|
self.add_depends(name, range.symbols())
|
|
@@ -481,7 +447,14 @@ class Scan():
|
|
|
481
447
|
|
|
482
448
|
self._variables = {'self': self}
|
|
483
449
|
|
|
484
|
-
|
|
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,
|
|
485
458
|
self.description['setters'])
|
|
486
459
|
for level, total in self.description['total'].items():
|
|
487
460
|
if total == np.inf:
|
|
@@ -640,51 +613,6 @@ class Scan():
|
|
|
640
613
|
return await awaitable
|
|
641
614
|
|
|
642
615
|
|
|
643
|
-
class Unpicklable:
|
|
644
|
-
|
|
645
|
-
def __init__(self, obj):
|
|
646
|
-
self.type = str(type(obj))
|
|
647
|
-
self.id = id(obj)
|
|
648
|
-
|
|
649
|
-
def __repr__(self):
|
|
650
|
-
return f'<Unpicklable: {self.type} at 0x{id(self):x}>'
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
class TooLarge:
|
|
654
|
-
|
|
655
|
-
def __init__(self, obj):
|
|
656
|
-
self.type = str(type(obj))
|
|
657
|
-
self.id = id(obj)
|
|
658
|
-
|
|
659
|
-
def __repr__(self):
|
|
660
|
-
return f'<TooLarge: {self.type} at 0x{id(self):x}>'
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
def dump_globals(ns=None, *, size_limit=10 * 1024 * 1024, warn=False):
|
|
664
|
-
import __main__
|
|
665
|
-
|
|
666
|
-
if ns is None:
|
|
667
|
-
ns = __main__.__dict__
|
|
668
|
-
|
|
669
|
-
namespace = {}
|
|
670
|
-
|
|
671
|
-
for name, value in ns.items():
|
|
672
|
-
try:
|
|
673
|
-
buf = dill.dumps(value)
|
|
674
|
-
except:
|
|
675
|
-
namespace[name] = Unpicklable(value)
|
|
676
|
-
if warn:
|
|
677
|
-
warnings.warn(f'Unpicklable: {name} {type(value)}')
|
|
678
|
-
if len(buf) > size_limit:
|
|
679
|
-
namespace[name] = TooLarge(value)
|
|
680
|
-
if warn:
|
|
681
|
-
warnings.warn(f'TooLarge: {name} {type(value)}')
|
|
682
|
-
else:
|
|
683
|
-
namespace[name] = buf
|
|
684
|
-
|
|
685
|
-
return namespace
|
|
686
|
-
|
|
687
|
-
|
|
688
616
|
def assymbly(description):
|
|
689
617
|
import __main__
|
|
690
618
|
from IPython import get_ipython
|
|
@@ -810,6 +738,38 @@ def assymbly(description):
|
|
|
810
738
|
if ready:
|
|
811
739
|
description['order'][level].append(ready)
|
|
812
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
|
+
|
|
813
773
|
return description
|
|
814
774
|
|
|
815
775
|
|
|
@@ -845,6 +805,8 @@ async def _iter_level(variables,
|
|
|
845
805
|
opts[iter.optimizer.name] = iter.optimizer.create()
|
|
846
806
|
elif isinstance(iter, Expression):
|
|
847
807
|
iters_d[name] = iter.eval(env)
|
|
808
|
+
elif isinstance(iter, Space):
|
|
809
|
+
iters_d[name] = iter.toarray()
|
|
848
810
|
elif callable(iter):
|
|
849
811
|
iters_d[name] = await call_function(iter, variables)
|
|
850
812
|
else:
|
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
|
|
qulab/scan/space.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
from typing import Type
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import skopt
|
|
5
|
+
from skopt.space import Categorical, Integer, Real
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Space():
|
|
9
|
+
|
|
10
|
+
def __init__(self, function, *args, **kwds):
|
|
11
|
+
self.function = function
|
|
12
|
+
self.args = args
|
|
13
|
+
self.kwds = kwds
|
|
14
|
+
|
|
15
|
+
def __repr__(self):
|
|
16
|
+
if self.function == 'asarray':
|
|
17
|
+
return repr(self.args[0])
|
|
18
|
+
args = ', '.join(map(repr, self.args))
|
|
19
|
+
kwds = ', '.join(f'{k}={v!r}' for k, v in self.kwds.items())
|
|
20
|
+
return f"{self.function}({args}, {kwds})"
|
|
21
|
+
|
|
22
|
+
def __len__(self):
|
|
23
|
+
return len(self.toarray())
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def fromarray(cls, array):
|
|
27
|
+
if isinstance(array, Space):
|
|
28
|
+
return array
|
|
29
|
+
if isinstance(array, (list, tuple)):
|
|
30
|
+
array = np.array(array)
|
|
31
|
+
try:
|
|
32
|
+
a = np.linspace(array[0], array[-1], len(array), dtype=array.dtype)
|
|
33
|
+
if np.allclose(a, array):
|
|
34
|
+
return cls('linspace',
|
|
35
|
+
array[0],
|
|
36
|
+
array[-1],
|
|
37
|
+
len(array),
|
|
38
|
+
dtype=array.dtype)
|
|
39
|
+
except:
|
|
40
|
+
pass
|
|
41
|
+
try:
|
|
42
|
+
a = np.logspace(np.log10(array[0]),
|
|
43
|
+
np.log10(array[-1]),
|
|
44
|
+
len(array),
|
|
45
|
+
base=10,
|
|
46
|
+
dtype=array.dtype)
|
|
47
|
+
if np.allclose(a, array):
|
|
48
|
+
return cls('logspace',
|
|
49
|
+
np.log10(array[0]),
|
|
50
|
+
np.log10(array[-1]),
|
|
51
|
+
len(array),
|
|
52
|
+
base=10,
|
|
53
|
+
dtype=array.dtype)
|
|
54
|
+
except:
|
|
55
|
+
pass
|
|
56
|
+
try:
|
|
57
|
+
a = np.logspace(np.log2(array[0]),
|
|
58
|
+
np.log2(array[-1]),
|
|
59
|
+
len(array),
|
|
60
|
+
base=2,
|
|
61
|
+
dtype=array.dtype)
|
|
62
|
+
if np.allclose(a, array):
|
|
63
|
+
return cls('logspace',
|
|
64
|
+
np.log2(array[0]),
|
|
65
|
+
np.log2(array[-1]),
|
|
66
|
+
len(array),
|
|
67
|
+
base=2,
|
|
68
|
+
dtype=array.dtype)
|
|
69
|
+
except:
|
|
70
|
+
pass
|
|
71
|
+
try:
|
|
72
|
+
a = np.geomspace(array[0],
|
|
73
|
+
array[-1],
|
|
74
|
+
len(array),
|
|
75
|
+
dtype=array.dtype)
|
|
76
|
+
if np.allclose(a, array):
|
|
77
|
+
return cls('geomspace',
|
|
78
|
+
array[0],
|
|
79
|
+
array[-1],
|
|
80
|
+
len(array),
|
|
81
|
+
dtype=array.dtype)
|
|
82
|
+
except:
|
|
83
|
+
pass
|
|
84
|
+
return array
|
|
85
|
+
|
|
86
|
+
def toarray(self):
|
|
87
|
+
return getattr(np, self.function)(*self.args, **self.kwds)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def logspace(start, stop, num=50, endpoint=True, base=10):
|
|
91
|
+
return Space('logspace', start, stop, num, endpoint=endpoint, base=base)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def linspace(start, stop, num=50, endpoint=True):
|
|
95
|
+
return Space('linspace', start, stop, num, endpoint=endpoint)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def geomspace(start, stop, num=50, endpoint=True):
|
|
99
|
+
return Space('geomspace', start, stop, num, endpoint=endpoint)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class OptimizeSpace():
|
|
103
|
+
|
|
104
|
+
def __init__(self, optimizer: 'Optimizer', space):
|
|
105
|
+
self.optimizer = optimizer
|
|
106
|
+
self.space = space
|
|
107
|
+
self.name = None
|
|
108
|
+
|
|
109
|
+
def __len__(self):
|
|
110
|
+
return self.optimizer.maxiter
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class Optimizer():
|
|
114
|
+
|
|
115
|
+
def __init__(self,
|
|
116
|
+
scanner,
|
|
117
|
+
name: str,
|
|
118
|
+
level: int,
|
|
119
|
+
method: str | Type = skopt.Optimizer,
|
|
120
|
+
maxiter: int = 1000,
|
|
121
|
+
minimize: bool = True,
|
|
122
|
+
**kwds):
|
|
123
|
+
self.scanner = scanner
|
|
124
|
+
self.method = method
|
|
125
|
+
self.maxiter = maxiter
|
|
126
|
+
self.dimensions = {}
|
|
127
|
+
self.name = name
|
|
128
|
+
self.level = level
|
|
129
|
+
self.kwds = kwds
|
|
130
|
+
self.minimize = minimize
|
|
131
|
+
|
|
132
|
+
def create(self):
|
|
133
|
+
return self.method(list(self.dimensions.values()), **self.kwds)
|
|
134
|
+
|
|
135
|
+
def Categorical(self,
|
|
136
|
+
categories,
|
|
137
|
+
prior=None,
|
|
138
|
+
transform=None,
|
|
139
|
+
name=None) -> OptimizeSpace:
|
|
140
|
+
return OptimizeSpace(self,
|
|
141
|
+
Categorical(categories, prior, transform, name))
|
|
142
|
+
|
|
143
|
+
def Integer(self,
|
|
144
|
+
low,
|
|
145
|
+
high,
|
|
146
|
+
prior="uniform",
|
|
147
|
+
base=10,
|
|
148
|
+
transform=None,
|
|
149
|
+
name=None,
|
|
150
|
+
dtype=np.int64) -> OptimizeSpace:
|
|
151
|
+
return OptimizeSpace(
|
|
152
|
+
self, Integer(low, high, prior, base, transform, name, dtype))
|
|
153
|
+
|
|
154
|
+
def Real(self,
|
|
155
|
+
low,
|
|
156
|
+
high,
|
|
157
|
+
prior="uniform",
|
|
158
|
+
base=10,
|
|
159
|
+
transform=None,
|
|
160
|
+
name=None,
|
|
161
|
+
dtype=float) -> OptimizeSpace:
|
|
162
|
+
return OptimizeSpace(
|
|
163
|
+
self, Real(low, high, prior, base, transform, name, dtype))
|
|
164
|
+
|
|
165
|
+
def __getstate__(self) -> dict:
|
|
166
|
+
state = self.__dict__.copy()
|
|
167
|
+
del state['scanner']
|
|
168
|
+
return state
|
|
169
|
+
|
|
170
|
+
def __setstate__(self, state: dict) -> None:
|
|
171
|
+
self.__dict__.update(state)
|
|
172
|
+
self.scanner = None
|
qulab/scan/utils.py
CHANGED
|
@@ -1,11 +1,59 @@
|
|
|
1
1
|
import ast
|
|
2
2
|
import asyncio
|
|
3
3
|
import inspect
|
|
4
|
+
import warnings
|
|
4
5
|
from typing import Any, Callable
|
|
5
6
|
|
|
7
|
+
import dill
|
|
8
|
+
|
|
6
9
|
from .expression import Env, Expression
|
|
7
10
|
|
|
8
11
|
|
|
12
|
+
class Unpicklable:
|
|
13
|
+
|
|
14
|
+
def __init__(self, obj):
|
|
15
|
+
self.type = str(type(obj))
|
|
16
|
+
self.id = id(obj)
|
|
17
|
+
|
|
18
|
+
def __repr__(self):
|
|
19
|
+
return f'<Unpicklable: {self.type} at 0x{id(self):x}>'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TooLarge:
|
|
23
|
+
|
|
24
|
+
def __init__(self, obj):
|
|
25
|
+
self.type = str(type(obj))
|
|
26
|
+
self.id = id(obj)
|
|
27
|
+
|
|
28
|
+
def __repr__(self):
|
|
29
|
+
return f'<TooLarge: {self.type} at 0x{id(self):x}>'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def dump_globals(ns=None, *, size_limit=10 * 1024 * 1024, warn=False):
|
|
33
|
+
import __main__
|
|
34
|
+
|
|
35
|
+
if ns is None:
|
|
36
|
+
ns = __main__.__dict__
|
|
37
|
+
|
|
38
|
+
namespace = {}
|
|
39
|
+
|
|
40
|
+
for name, value in ns.items():
|
|
41
|
+
try:
|
|
42
|
+
buf = dill.dumps(value)
|
|
43
|
+
except:
|
|
44
|
+
namespace[name] = Unpicklable(value)
|
|
45
|
+
if warn:
|
|
46
|
+
warnings.warn(f'Unpicklable: {name} {type(value)}')
|
|
47
|
+
if len(buf) > size_limit:
|
|
48
|
+
namespace[name] = TooLarge(value)
|
|
49
|
+
if warn:
|
|
50
|
+
warnings.warn(f'TooLarge: {name} {type(value)}')
|
|
51
|
+
else:
|
|
52
|
+
namespace[name] = buf
|
|
53
|
+
|
|
54
|
+
return namespace
|
|
55
|
+
|
|
56
|
+
|
|
9
57
|
def is_valid_identifier(s: str) -> bool:
|
|
10
58
|
"""
|
|
11
59
|
Check if a string is a valid identifier.
|
qulab/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.1.
|
|
1
|
+
__version__ = "2.1.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|