QuLab 2.9.6__cp310-cp310-macosx_10_9_universal2.whl → 2.9.8__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/__init__.py +1 -0
- qulab/executor/analyze.py +164 -0
- qulab/executor/cli.py +16 -5
- qulab/executor/transform.py +46 -3
- qulab/fun.cpython-310-darwin.so +0 -0
- qulab/version.py +1 -1
- {qulab-2.9.6.dist-info → qulab-2.9.8.dist-info}/METADATA +2 -1
- {qulab-2.9.6.dist-info → qulab-2.9.8.dist-info}/RECORD +12 -11
- {qulab-2.9.6.dist-info → qulab-2.9.8.dist-info}/WHEEL +0 -0
- {qulab-2.9.6.dist-info → qulab-2.9.8.dist-info}/entry_points.txt +0 -0
- {qulab-2.9.6.dist-info → qulab-2.9.8.dist-info}/licenses/LICENSE +0 -0
- {qulab-2.9.6.dist-info → qulab-2.9.8.dist-info}/top_level.txt +0 -0
qulab/__init__.py
CHANGED
@@ -0,0 +1,164 @@
|
|
1
|
+
import pickle
|
2
|
+
import threading
|
3
|
+
import time
|
4
|
+
|
5
|
+
import pyperclip
|
6
|
+
import zmq
|
7
|
+
|
8
|
+
from .storage import Report
|
9
|
+
|
10
|
+
# 需要复制到 Notebook 的代码模板
|
11
|
+
clip_template = """
|
12
|
+
from qulab.executor.analyze import get_report as get_report_{server_port}
|
13
|
+
|
14
|
+
report, history = get_report_{server_port}("tcp://{server_address}:{server_port}")
|
15
|
+
# 在这里插入数据处理逻辑
|
16
|
+
|
17
|
+
{analysis_code}
|
18
|
+
"""
|
19
|
+
|
20
|
+
analysis_code = """
|
21
|
+
# report.parameters = {}
|
22
|
+
# report.oracle = {}
|
23
|
+
"""
|
24
|
+
|
25
|
+
|
26
|
+
# ZeroMQ 服务线程,用于响应 Notebook 的请求
|
27
|
+
class ServerThread(threading.Thread):
|
28
|
+
|
29
|
+
def __init__(self, data, timeout):
|
30
|
+
super().__init__()
|
31
|
+
self.data = data
|
32
|
+
self.result = None
|
33
|
+
self.port = 0
|
34
|
+
self.timeout = timeout
|
35
|
+
self.running = True
|
36
|
+
self.finished = threading.Event()
|
37
|
+
self.context = zmq.Context()
|
38
|
+
|
39
|
+
def find_free_port(self):
|
40
|
+
with zmq.Socket(self.context, zmq.REQ) as s:
|
41
|
+
s.bind_to_random_port("tcp://*")
|
42
|
+
self.port = int(
|
43
|
+
s.getsockopt(zmq.LAST_ENDPOINT).decode().split(":")[-1])
|
44
|
+
s.unbind(s.getsockopt(zmq.LAST_ENDPOINT))
|
45
|
+
return self.port
|
46
|
+
|
47
|
+
def run(self):
|
48
|
+
self.port = self.find_free_port()
|
49
|
+
socket = self.context.socket(zmq.REP)
|
50
|
+
socket.bind(f"tcp://*:{self.port}")
|
51
|
+
# 设置 recv 超时 1 秒
|
52
|
+
socket.RCVTIMEO = 1000
|
53
|
+
start_time = time.time()
|
54
|
+
try:
|
55
|
+
while self.running and (time.time() - start_time < self.timeout):
|
56
|
+
try:
|
57
|
+
msg = socket.recv()
|
58
|
+
except zmq.Again:
|
59
|
+
continue # 超时后继续等待
|
60
|
+
|
61
|
+
# Notebook 端请求数据
|
62
|
+
if msg == b"GET":
|
63
|
+
socket.send(pickle.dumps(self.data))
|
64
|
+
else:
|
65
|
+
# Notebook 端提交了处理结果
|
66
|
+
try:
|
67
|
+
self.result = pickle.loads(msg)
|
68
|
+
except Exception as e:
|
69
|
+
# 如果解析失败,也返回默认 ACK
|
70
|
+
self.result = None
|
71
|
+
socket.send(b"ACK")
|
72
|
+
self.running = False
|
73
|
+
break
|
74
|
+
finally:
|
75
|
+
socket.close()
|
76
|
+
self.context.term()
|
77
|
+
self.finished.set()
|
78
|
+
|
79
|
+
def stop(self):
|
80
|
+
self.running = False
|
81
|
+
self.finished.set()
|
82
|
+
|
83
|
+
def wait_for_result(self):
|
84
|
+
self.finished.wait()
|
85
|
+
return self.result
|
86
|
+
|
87
|
+
|
88
|
+
# 进入分析流程,启动服务并打印等待提示
|
89
|
+
def get_result_or_wait_until_timeout(data: tuple[Report, Report | None],
|
90
|
+
timeout: float) -> Report:
|
91
|
+
server = ServerThread(data, timeout)
|
92
|
+
server.start()
|
93
|
+
|
94
|
+
# 格式化代码模板
|
95
|
+
code = clip_template.format(server_address="127.0.0.1",
|
96
|
+
server_port=server.port,
|
97
|
+
analysis_code=analysis_code)
|
98
|
+
|
99
|
+
# 将代码复制到剪切板
|
100
|
+
pyperclip.copy(code)
|
101
|
+
|
102
|
+
# 同时打印到终端,防止误操作导致剪切板内容丢失
|
103
|
+
print("请在 Jupyter Notebook 中运行下面这段代码:")
|
104
|
+
print("-" * 60)
|
105
|
+
print(code)
|
106
|
+
print("-" * 60)
|
107
|
+
print("等待 Notebook 提交处理结果,或等待超时({} 秒)...".format(timeout))
|
108
|
+
|
109
|
+
start_time = time.time()
|
110
|
+
# 采用循环等待提交结果,间隔 0.5 秒检测一次
|
111
|
+
while server.finished.wait(timeout=0.5) is False:
|
112
|
+
elapsed = time.time() - start_time
|
113
|
+
if elapsed >= timeout:
|
114
|
+
# 超时后结束等待
|
115
|
+
server.stop()
|
116
|
+
break
|
117
|
+
|
118
|
+
result = server.wait_for_result()
|
119
|
+
return result if result is not None else data
|
120
|
+
|
121
|
+
|
122
|
+
def manual_analysis(report: Report, history=None, timeout=3600):
|
123
|
+
try:
|
124
|
+
report = get_result_or_wait_until_timeout((report, history), timeout)
|
125
|
+
except Exception as e:
|
126
|
+
pass
|
127
|
+
return report
|
128
|
+
|
129
|
+
|
130
|
+
def get_report(address: str) -> Report:
|
131
|
+
import IPython
|
132
|
+
|
133
|
+
ipy = IPython.get_ipython()
|
134
|
+
if ipy is None:
|
135
|
+
raise RuntimeError("请在 Jupyter Notebook 中运行此函数。")
|
136
|
+
ipy.set_next_input(("from qulab.executor.analyze import submit_report\n"
|
137
|
+
"# 处理完成后,提交结果\n"
|
138
|
+
f"# submit_report(report, \"{address}\")"),
|
139
|
+
replace=False)
|
140
|
+
context = zmq.Context()
|
141
|
+
sock = context.socket(zmq.REQ)
|
142
|
+
sock.connect(address)
|
143
|
+
# 请求数据
|
144
|
+
sock.send(b"GET")
|
145
|
+
report, history = pickle.loads(sock.recv())
|
146
|
+
return report, history
|
147
|
+
|
148
|
+
|
149
|
+
def submit_report(report: Report, address: str):
|
150
|
+
import IPython
|
151
|
+
|
152
|
+
ipy = IPython.get_ipython()
|
153
|
+
if ipy is None:
|
154
|
+
raise RuntimeError("请在 Jupyter Notebook 中运行此函数。")
|
155
|
+
|
156
|
+
# ipy.user_ns['In'][-2]
|
157
|
+
|
158
|
+
context = zmq.Context()
|
159
|
+
sock = context.socket(zmq.REQ)
|
160
|
+
sock.connect(address)
|
161
|
+
# 提交处理后的结果
|
162
|
+
sock.send(pickle.dumps(report))
|
163
|
+
ack = sock.recv()
|
164
|
+
print("结果已提交。")
|
qulab/executor/cli.py
CHANGED
@@ -126,7 +126,8 @@ def set(key, value, api):
|
|
126
126
|
from . import transform
|
127
127
|
if api is not None:
|
128
128
|
api = importlib.import_module(api)
|
129
|
-
set_config_api(api.query_config, api.update_config, api.
|
129
|
+
set_config_api(api.query_config, api.update_config, api.delete_config,
|
130
|
+
api.export_config, api.clear_config)
|
130
131
|
try:
|
131
132
|
value = eval(value)
|
132
133
|
except:
|
@@ -148,7 +149,8 @@ def get(key, api):
|
|
148
149
|
from . import transform
|
149
150
|
if api is not None:
|
150
151
|
api = importlib.import_module(api)
|
151
|
-
set_config_api(api.query_config, api.update_config, api.
|
152
|
+
set_config_api(api.query_config, api.update_config, api.delete_config,
|
153
|
+
api.export_config, api.clear_config)
|
152
154
|
click.echo(transform.query_config(key))
|
153
155
|
|
154
156
|
|
@@ -184,7 +186,8 @@ def run(workflow, code, data, api, plot, no_dependents, retry, freeze):
|
|
184
186
|
f'{" --freeze " if freeze else ""}')
|
185
187
|
if api is not None:
|
186
188
|
api = importlib.import_module(api)
|
187
|
-
set_config_api(api.query_config, api.update_config, api.
|
189
|
+
set_config_api(api.query_config, api.update_config, api.delete_config,
|
190
|
+
api.export_config, api.clear_config)
|
188
191
|
if code is None:
|
189
192
|
code = Path.cwd()
|
190
193
|
if data is None:
|
@@ -255,7 +258,8 @@ def maintain(workflow, code, data, api, retry, plot):
|
|
255
258
|
f'{" --plot" if plot else ""}')
|
256
259
|
if api is not None:
|
257
260
|
api = importlib.import_module(api)
|
258
|
-
set_config_api(api.query_config, api.update_config, api.
|
261
|
+
set_config_api(api.query_config, api.update_config, api.delete_config,
|
262
|
+
api.export_config, api.clear_config)
|
259
263
|
if code is None:
|
260
264
|
code = Path.cwd()
|
261
265
|
if data is None:
|
@@ -311,7 +315,8 @@ def reproduce(report_id, code, data, api, plot):
|
|
311
315
|
f'{" --plot" if plot else ""}')
|
312
316
|
if api is not None:
|
313
317
|
api = importlib.import_module(api)
|
314
|
-
set_config_api(api.query_config, api.update_config, api.
|
318
|
+
set_config_api(api.query_config, api.update_config, api.delete_config,
|
319
|
+
api.export_config, api.clear_config)
|
315
320
|
if code is None:
|
316
321
|
code = Path.cwd()
|
317
322
|
if data is None:
|
@@ -320,10 +325,16 @@ def reproduce(report_id, code, data, api, plot):
|
|
320
325
|
code = Path(os.path.expanduser(code))
|
321
326
|
data = Path(os.path.expanduser(data))
|
322
327
|
|
328
|
+
from . import transform
|
323
329
|
from .load import load_workflow_from_source_code
|
324
330
|
from .storage import get_report_by_index
|
325
331
|
|
326
332
|
r = get_report_by_index(int(report_id), data)
|
327
333
|
|
328
334
|
wf = load_workflow_from_source_code(r.workflow, r.script)
|
335
|
+
cfg = transform.export_config()
|
336
|
+
transform.clear_config()
|
337
|
+
transform.update_config(r.config)
|
329
338
|
run_workflow(wf, code, data, plot=plot, freeze=True)
|
339
|
+
transform.clear_config()
|
340
|
+
transform.update_config(cfg)
|
qulab/executor/transform.py
CHANGED
@@ -31,6 +31,22 @@ def _update_config(updates):
|
|
31
31
|
pickle.dump(parameters, f)
|
32
32
|
|
33
33
|
|
34
|
+
def _delete_config(name: str):
|
35
|
+
import pickle
|
36
|
+
|
37
|
+
try:
|
38
|
+
with open('parameters.pkl', 'rb') as f:
|
39
|
+
parameters = pickle.load(f)
|
40
|
+
except:
|
41
|
+
parameters = {}
|
42
|
+
|
43
|
+
if name in parameters:
|
44
|
+
del parameters[name]
|
45
|
+
|
46
|
+
with open('parameters.pkl', 'wb') as f:
|
47
|
+
pickle.dump(parameters, f)
|
48
|
+
|
49
|
+
|
34
50
|
def _export_config() -> dict:
|
35
51
|
import pickle
|
36
52
|
|
@@ -43,6 +59,21 @@ def _export_config() -> dict:
|
|
43
59
|
return parameters
|
44
60
|
|
45
61
|
|
62
|
+
def _clear_config():
|
63
|
+
import pickle
|
64
|
+
|
65
|
+
try:
|
66
|
+
with open('parameters.pkl', 'rb') as f:
|
67
|
+
parameters = pickle.load(f)
|
68
|
+
except:
|
69
|
+
parameters = {}
|
70
|
+
|
71
|
+
parameters.clear()
|
72
|
+
|
73
|
+
with open('parameters.pkl', 'wb') as f:
|
74
|
+
pickle.dump(parameters, f)
|
75
|
+
|
76
|
+
|
46
77
|
def obey_the_oracle(report: Report, data_path):
|
47
78
|
global __current_config_id
|
48
79
|
update_config(report.oracle)
|
@@ -67,10 +98,16 @@ def current_config(data_path):
|
|
67
98
|
|
68
99
|
query_config = _query_config
|
69
100
|
update_config = _update_config
|
101
|
+
delete_config = _delete_config
|
70
102
|
export_config = _export_config
|
103
|
+
clear_config = _clear_config
|
71
104
|
|
72
105
|
|
73
|
-
def set_config_api(query_method,
|
106
|
+
def set_config_api(query_method,
|
107
|
+
update_method,
|
108
|
+
delete_method,
|
109
|
+
export_method,
|
110
|
+
clear_method=None):
|
74
111
|
"""
|
75
112
|
Set the query and update methods for the config.
|
76
113
|
|
@@ -79,13 +116,19 @@ def set_config_api(query_method, update_method, export_method):
|
|
79
116
|
the method should take a key and return the value.
|
80
117
|
update_method: The update method.
|
81
118
|
the method should take a dict of updates.
|
119
|
+
delete_method: The delete method.
|
120
|
+
the method should take a key and delete it.
|
82
121
|
export_method: The export method.
|
83
122
|
the method should return a dict of the config.
|
123
|
+
clear_method: The clear method.
|
124
|
+
the method should clear the config.
|
84
125
|
"""
|
85
|
-
global query_config, update_config, export_config
|
126
|
+
global query_config, update_config, delete_config, export_config, clear_config
|
86
127
|
|
87
128
|
query_config = query_method
|
88
129
|
update_config = update_method
|
130
|
+
delete_config = delete_method
|
89
131
|
export_config = export_method
|
132
|
+
clear_config = clear_method
|
90
133
|
|
91
|
-
return query_config, update_config, export_config
|
134
|
+
return query_config, update_config, delete_config, export_config, clear_config
|
qulab/fun.cpython-310-darwin.so
CHANGED
Binary file
|
qulab/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "2.9.
|
1
|
+
__version__ = "2.9.8"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: QuLab
|
3
|
-
Version: 2.9.
|
3
|
+
Version: 2.9.8
|
4
4
|
Summary: contral instruments and manage data
|
5
5
|
Author-email: feihoo87 <feihoo87@gmail.com>
|
6
6
|
Maintainer-email: feihoo87 <feihoo87@gmail.com>
|
@@ -36,6 +36,7 @@ Requires-Dist: msgpack>=1.0.5
|
|
36
36
|
Requires-Dist: nevergrad>=1.0.2
|
37
37
|
Requires-Dist: numpy>=1.13.3
|
38
38
|
Requires-Dist: ply>=3.11
|
39
|
+
Requires-Dist: pyperclip>=1.8.2
|
39
40
|
Requires-Dist: pyzmq>=25.1.0
|
40
41
|
Requires-Dist: scipy>=1.0.0
|
41
42
|
Requires-Dist: scikit-optimize>=0.9.0
|
@@ -1,20 +1,21 @@
|
|
1
|
-
qulab/__init__.py,sha256=
|
1
|
+
qulab/__init__.py,sha256=fL8FSMGk4vzTMXLxWgNHpd1fNWau4yujSWjPEPqudos,367
|
2
2
|
qulab/__main__.py,sha256=fjaRSL_uUjNIzBGNgjlGswb9TJ2VD5qnkZHW3hItrD4,68
|
3
3
|
qulab/dicttree.py,sha256=tRRMpGZYVOLw0TEByE3_2Ss8FdOmzuGL9e1DWbs8qoY,13684
|
4
|
-
qulab/fun.cpython-310-darwin.so,sha256=
|
4
|
+
qulab/fun.cpython-310-darwin.so,sha256=m-rRfd0TyCJqh8UIFhqhsrpuk0rO9DGLZRvBO5bV4OM,126864
|
5
5
|
qulab/typing.py,sha256=vg62sGqxuD9CI5677ejlzAmf2fVdAESZCQjAE_xSxPg,69
|
6
6
|
qulab/utils.py,sha256=BdLdlfjpe6m6gSeONYmpAKTTqxDaYHNk4exlz8kZxTg,2982
|
7
|
-
qulab/version.py,sha256=
|
7
|
+
qulab/version.py,sha256=yIBbBBe2GVrcVKOqSi44N9VxQXXdByyDHw1T917V4HQ,21
|
8
8
|
qulab/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
qulab/cli/commands.py,sha256=F1LATmSC9lOAdnrOTMK7DRjETCEcOmMsocovWRyjWTc,597
|
10
10
|
qulab/cli/config.py,sha256=Ei7eSYnbwPPlluDnm8YmWONYiI4g7WtvlZGQdr1Z6vo,3688
|
11
11
|
qulab/executor/__init__.py,sha256=LosPzOMaljSZY1thy_Fxtbrgq7uubJszMABEB7oM7tU,101
|
12
|
-
qulab/executor/
|
12
|
+
qulab/executor/analyze.py,sha256=Q3wL4a1dPm8WkVLlE0tTjvD5MhV-Ic9ZQ6cNXbZsqWE,4960
|
13
|
+
qulab/executor/cli.py,sha256=xceK6XNLnNpAVtqvs0_wOAuG0sgS9jg1iTFYmxVUMWY,11795
|
13
14
|
qulab/executor/load.py,sha256=qI-3b3xvsjZMCthRO3hlzHA0BZI-tH9Y_L0XLBcyWV8,18657
|
14
15
|
qulab/executor/schedule.py,sha256=XRimchHYCgnMAOtCvNLjwMv9IkcTAyBAUCHKQs5RBRw,18745
|
15
16
|
qulab/executor/storage.py,sha256=PWQIDYjQaoyLGgAKh0X1tlNQTgDWR8bI-HVie4hSkyA,21075
|
16
17
|
qulab/executor/template.py,sha256=_HEtsUQ5_jSujCw8FBDAK1PRTMRCa4iD4DduHIpjo3c,10569
|
17
|
-
qulab/executor/transform.py,sha256=
|
18
|
+
qulab/executor/transform.py,sha256=rk4CLIKVjGRaFzi5FVSgadUxAKKVLSopEHZCaAzDwDg,3435
|
18
19
|
qulab/executor/utils.py,sha256=l_b0y2kMwYKyyXeFtoblPYwKNU-wiFQ9PMo9QlWl9wE,6213
|
19
20
|
qulab/monitor/__init__.py,sha256=nTHelnDpxRS_fl_B38TsN0njgq8eVTEz9IAnN3NbDlM,42
|
20
21
|
qulab/monitor/__main__.py,sha256=w3yUcqq195LzSnXTkQcuC1RSFRhy4oQ_PEBmucXguME,97
|
@@ -97,9 +98,9 @@ qulab/visualization/plot_seq.py,sha256=UWTS6p9nfX_7B8ehcYo6UnSTUCjkBsNU9jiOeW2ca
|
|
97
98
|
qulab/visualization/qdat.py,sha256=ZeevBYWkzbww4xZnsjHhw7wRorJCBzbG0iEu-XQB4EA,5735
|
98
99
|
qulab/visualization/rot3d.py,sha256=lMrEJlRLwYe6NMBlGkKYpp_V9CTipOAuDy6QW_cQK00,734
|
99
100
|
qulab/visualization/widgets.py,sha256=6KkiTyQ8J-ei70LbPQZAK35wjktY47w2IveOa682ftA,3180
|
100
|
-
qulab-2.9.
|
101
|
-
qulab-2.9.
|
102
|
-
qulab-2.9.
|
103
|
-
qulab-2.9.
|
104
|
-
qulab-2.9.
|
105
|
-
qulab-2.9.
|
101
|
+
qulab-2.9.8.dist-info/licenses/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
|
102
|
+
qulab-2.9.8.dist-info/METADATA,sha256=sF1gAnVrRIYx11xGMiLpXJkWp3MypNa5g3tGHsHK10M,3752
|
103
|
+
qulab-2.9.8.dist-info/WHEEL,sha256=qnIvK8L8kOW9FTrzifubcmNsZckgNfGDNKzGG7FMEdE,114
|
104
|
+
qulab-2.9.8.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
|
105
|
+
qulab-2.9.8.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
|
106
|
+
qulab-2.9.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|