QuLab 2.10.10__cp313-cp313-macosx_10_13_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 +33 -0
- qulab/__main__.py +4 -0
- qulab/cli/__init__.py +0 -0
- qulab/cli/commands.py +30 -0
- qulab/cli/config.py +170 -0
- qulab/cli/decorators.py +28 -0
- qulab/dicttree.py +523 -0
- qulab/executor/__init__.py +5 -0
- qulab/executor/analyze.py +188 -0
- qulab/executor/cli.py +434 -0
- qulab/executor/load.py +563 -0
- qulab/executor/registry.py +185 -0
- qulab/executor/schedule.py +543 -0
- qulab/executor/storage.py +615 -0
- qulab/executor/template.py +259 -0
- qulab/executor/utils.py +194 -0
- qulab/expression.py +827 -0
- qulab/fun.cpython-313-darwin.so +0 -0
- qulab/monitor/__init__.py +1 -0
- qulab/monitor/__main__.py +8 -0
- qulab/monitor/config.py +41 -0
- qulab/monitor/dataset.py +77 -0
- qulab/monitor/event_queue.py +54 -0
- qulab/monitor/mainwindow.py +234 -0
- qulab/monitor/monitor.py +115 -0
- qulab/monitor/ploter.py +123 -0
- qulab/monitor/qt_compat.py +16 -0
- qulab/monitor/toolbar.py +265 -0
- qulab/scan/__init__.py +2 -0
- qulab/scan/curd.py +221 -0
- qulab/scan/models.py +554 -0
- qulab/scan/optimize.py +76 -0
- qulab/scan/query.py +387 -0
- qulab/scan/record.py +603 -0
- qulab/scan/scan.py +1166 -0
- qulab/scan/server.py +450 -0
- qulab/scan/space.py +213 -0
- qulab/scan/utils.py +234 -0
- qulab/storage/__init__.py +0 -0
- qulab/storage/__main__.py +51 -0
- qulab/storage/backend/__init__.py +0 -0
- qulab/storage/backend/redis.py +204 -0
- qulab/storage/base_dataset.py +352 -0
- qulab/storage/chunk.py +60 -0
- qulab/storage/dataset.py +127 -0
- qulab/storage/file.py +273 -0
- qulab/storage/models/__init__.py +22 -0
- qulab/storage/models/base.py +4 -0
- qulab/storage/models/config.py +28 -0
- qulab/storage/models/file.py +89 -0
- qulab/storage/models/ipy.py +58 -0
- qulab/storage/models/models.py +88 -0
- qulab/storage/models/record.py +161 -0
- qulab/storage/models/report.py +22 -0
- qulab/storage/models/tag.py +93 -0
- qulab/storage/storage.py +95 -0
- qulab/sys/__init__.py +2 -0
- qulab/sys/chat.py +688 -0
- qulab/sys/device/__init__.py +3 -0
- qulab/sys/device/basedevice.py +255 -0
- qulab/sys/device/loader.py +86 -0
- qulab/sys/device/utils.py +79 -0
- qulab/sys/drivers/FakeInstrument.py +68 -0
- qulab/sys/drivers/__init__.py +0 -0
- qulab/sys/ipy_events.py +125 -0
- qulab/sys/net/__init__.py +0 -0
- qulab/sys/net/bencoder.py +205 -0
- qulab/sys/net/cli.py +169 -0
- qulab/sys/net/dhcp.py +543 -0
- qulab/sys/net/dhcpd.py +176 -0
- qulab/sys/net/kad.py +1142 -0
- qulab/sys/net/kcp.py +192 -0
- qulab/sys/net/nginx.py +194 -0
- qulab/sys/progress.py +190 -0
- qulab/sys/rpc/__init__.py +0 -0
- qulab/sys/rpc/client.py +0 -0
- qulab/sys/rpc/exceptions.py +96 -0
- qulab/sys/rpc/msgpack.py +1052 -0
- qulab/sys/rpc/msgpack.pyi +41 -0
- qulab/sys/rpc/router.py +35 -0
- qulab/sys/rpc/rpc.py +412 -0
- qulab/sys/rpc/serialize.py +139 -0
- qulab/sys/rpc/server.py +29 -0
- qulab/sys/rpc/socket.py +29 -0
- qulab/sys/rpc/utils.py +25 -0
- qulab/sys/rpc/worker.py +0 -0
- qulab/sys/rpc/zmq_socket.py +227 -0
- qulab/tools/__init__.py +0 -0
- qulab/tools/connection_helper.py +39 -0
- qulab/typing.py +2 -0
- qulab/utils.py +95 -0
- qulab/version.py +1 -0
- qulab/visualization/__init__.py +188 -0
- qulab/visualization/__main__.py +71 -0
- qulab/visualization/_autoplot.py +464 -0
- qulab/visualization/plot_circ.py +319 -0
- qulab/visualization/plot_layout.py +408 -0
- qulab/visualization/plot_seq.py +242 -0
- qulab/visualization/qdat.py +152 -0
- qulab/visualization/rot3d.py +23 -0
- qulab/visualization/widgets.py +86 -0
- qulab-2.10.10.dist-info/METADATA +110 -0
- qulab-2.10.10.dist-info/RECORD +107 -0
- qulab-2.10.10.dist-info/WHEEL +5 -0
- qulab-2.10.10.dist-info/entry_points.txt +2 -0
- qulab-2.10.10.dist-info/licenses/LICENSE +21 -0
- qulab-2.10.10.dist-info/top_level.txt +1 -0
qulab/sys/rpc/worker.py
ADDED
File without changes
|
@@ -0,0 +1,227 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
import zmq
|
4
|
+
import zmq.asyncio
|
5
|
+
import zmq.auth
|
6
|
+
from watchdog.events import FileSystemEventHandler
|
7
|
+
from watchdog.observers import Observer
|
8
|
+
from zmq.auth.asyncio import AsyncioAuthenticator
|
9
|
+
from zmq.auth.thread import ThreadAuthenticator
|
10
|
+
|
11
|
+
|
12
|
+
class ReloadCertificatesHandler(FileSystemEventHandler):
|
13
|
+
|
14
|
+
def __init__(self, zmq: 'ZMQContextManager'):
|
15
|
+
self.zmq = zmq
|
16
|
+
|
17
|
+
def on_modified(self, event):
|
18
|
+
self.zmq.reload_certificates()
|
19
|
+
|
20
|
+
|
21
|
+
class ZMQContextManager:
|
22
|
+
"""
|
23
|
+
A context manager for managing ZeroMQ sockets with asynchronous support.
|
24
|
+
It handles the creation, connection, binding, and security configuration of
|
25
|
+
the sockets and ensures proper resource cleanup.
|
26
|
+
|
27
|
+
The context manager can be used as a synchronous context manager or an
|
28
|
+
asynchronous context manager. When used as an asynchronous context manager,
|
29
|
+
the socket is created with an asyncio context and can be used with the
|
30
|
+
`await` keyword.
|
31
|
+
|
32
|
+
The security settings for the socket can be configured using the secret key
|
33
|
+
and public key parameters. If the secret key is provided, the socket is
|
34
|
+
configured to use ZeroMQ curve encryption. The public key of the server can
|
35
|
+
also be provided for client sockets to connect to a server with curve
|
36
|
+
encryption. You can also provide the paths to the secret key and public key
|
37
|
+
files, and the public key of the server to load the keys from files. The
|
38
|
+
keys can be reloaded automatically when the files are modified by setting
|
39
|
+
the `public_keys_location` parameter.
|
40
|
+
|
41
|
+
To generate a secret key and public key pair, you can use the following
|
42
|
+
commands:
|
43
|
+
|
44
|
+
```bash
|
45
|
+
# Generate a secret key and public key pair
|
46
|
+
python -c "import zmq.auth; zmq.auth.create_certificates('.', 'filename')"
|
47
|
+
```
|
48
|
+
|
49
|
+
Attributes:
|
50
|
+
socket_type: zmq.SocketType
|
51
|
+
The type of the socket to create (e.g., zmq.REP).
|
52
|
+
bind: str, optional
|
53
|
+
The address to bind the socket to.
|
54
|
+
connect: str, optional
|
55
|
+
The address to connect the socket to.
|
56
|
+
secret_key_file: str, optional
|
57
|
+
The path to the secret key file for ZeroMQ curve encryption.
|
58
|
+
server_public_key_file: str, optional
|
59
|
+
The path to the public key file for the server in ZeroMQ curve
|
60
|
+
encryption.
|
61
|
+
public_keys_location: str, optional
|
62
|
+
The location to store the public keys for ZeroMQ curve encryption.
|
63
|
+
secret_key: bytes, optional
|
64
|
+
The secret key for ZeroMQ curve encryption.
|
65
|
+
public_key: bytes, optional
|
66
|
+
The public key for ZeroMQ curve encryption.
|
67
|
+
server_public_key: bytes, optional
|
68
|
+
The public key for the server in ZeroMQ curve encryption.
|
69
|
+
|
70
|
+
Methods:
|
71
|
+
_create_socket: zmq.Socket
|
72
|
+
Creates and configures a ZeroMQ socket.
|
73
|
+
_close_socket: None
|
74
|
+
Closes the ZeroMQ socket and the context, and stops the authenticator
|
75
|
+
if it was started.
|
76
|
+
|
77
|
+
Examples:
|
78
|
+
Create a REP socket and bind it to an address:
|
79
|
+
|
80
|
+
>>> async with ZMQContextManager(zmq.REP, bind='tcp://*:5555') as socket:
|
81
|
+
... while True:
|
82
|
+
... message = await socket.recv()
|
83
|
+
... await socket.send(message)
|
84
|
+
|
85
|
+
Create a REQ socket and connect it to an address:
|
86
|
+
|
87
|
+
>>> with ZMQContextManager(zmq.REQ, connect='tcp://localhost:5555') as socket:
|
88
|
+
... socket.send(b'Hello')
|
89
|
+
... message = socket.recv()
|
90
|
+
"""
|
91
|
+
|
92
|
+
def __init__(self,
|
93
|
+
socket_type: zmq.SocketType,
|
94
|
+
bind: Optional[str] = None,
|
95
|
+
connect: Optional[str] = None,
|
96
|
+
secret_key_file: Optional[str] = None,
|
97
|
+
server_public_key_file: Optional[str] = None,
|
98
|
+
public_keys_location: Optional[str] = None,
|
99
|
+
secret_key: Optional[bytes] = None,
|
100
|
+
public_key: Optional[bytes] = None,
|
101
|
+
server_public_key: Optional[bytes] = None,
|
102
|
+
socket: Optional[zmq.Socket] = None,
|
103
|
+
timeout: Optional[float] = None):
|
104
|
+
self.socket_type = socket_type
|
105
|
+
if bind is None and connect is None:
|
106
|
+
raise ValueError("Either 'bind' or 'connect' must be specified.")
|
107
|
+
if bind is not None and connect is not None:
|
108
|
+
raise ValueError("Both 'bind' and 'connect' cannot be specified.")
|
109
|
+
self.bind = bind
|
110
|
+
self.connect = connect
|
111
|
+
self.secret_key = secret_key
|
112
|
+
self.public_key = public_key
|
113
|
+
self.server_public_key = server_public_key
|
114
|
+
self.timeout = timeout
|
115
|
+
|
116
|
+
if secret_key_file:
|
117
|
+
self.public_key, self.secret_key = zmq.auth.load_certificate(
|
118
|
+
secret_key_file)
|
119
|
+
|
120
|
+
if (self.secret_key is not None and self.public_key is None
|
121
|
+
or self.secret_key is None and self.public_key is not None):
|
122
|
+
raise ValueError(
|
123
|
+
"Both secret key and public key must be specified.")
|
124
|
+
|
125
|
+
if server_public_key_file:
|
126
|
+
self.server_public_key = zmq.auth.load_certificate(
|
127
|
+
server_public_key_file)[0]
|
128
|
+
|
129
|
+
self.public_keys_location = public_keys_location
|
130
|
+
|
131
|
+
self.observer = None
|
132
|
+
self.auth = None
|
133
|
+
self.context = None
|
134
|
+
self.socket = None
|
135
|
+
self._external_socket = None
|
136
|
+
try:
|
137
|
+
if not socket.closed:
|
138
|
+
self._external_socket = socket
|
139
|
+
except:
|
140
|
+
pass
|
141
|
+
|
142
|
+
def _create_socket(self, asyncio=False) -> zmq.Socket:
|
143
|
+
"""
|
144
|
+
Creates and configures a ZeroMQ socket. Sets up security if required,
|
145
|
+
and binds or connects the socket according to the specified settings.
|
146
|
+
|
147
|
+
Returns:
|
148
|
+
zmq.Socket: The configured ZeroMQ socket.
|
149
|
+
"""
|
150
|
+
if self._external_socket:
|
151
|
+
return self._external_socket
|
152
|
+
if asyncio:
|
153
|
+
self.context = zmq.asyncio.Context()
|
154
|
+
else:
|
155
|
+
self.context = zmq.Context()
|
156
|
+
|
157
|
+
self.socket = self.context.socket(self.socket_type)
|
158
|
+
self.auth = None
|
159
|
+
|
160
|
+
if self.bind and self.secret_key:
|
161
|
+
if asyncio:
|
162
|
+
self.auth = AsyncioAuthenticator(self.context)
|
163
|
+
else:
|
164
|
+
self.auth = ThreadAuthenticator(self.context)
|
165
|
+
self.auth.start()
|
166
|
+
self.reload_certificates()
|
167
|
+
self.auto_reload_certificates()
|
168
|
+
self.socket.curve_server = True # must come before bind
|
169
|
+
|
170
|
+
if self.secret_key:
|
171
|
+
self.socket.curve_secretkey = self.secret_key
|
172
|
+
self.socket.curve_publickey = self.public_key
|
173
|
+
|
174
|
+
if self.bind:
|
175
|
+
self.socket.bind(self.bind)
|
176
|
+
if self.connect:
|
177
|
+
if self.server_public_key:
|
178
|
+
self.socket.curve_serverkey = self.server_public_key
|
179
|
+
self.socket.connect(self.connect)
|
180
|
+
if self.timeout:
|
181
|
+
timeout_ms = int(self.timeout * 1000)
|
182
|
+
self.socket.setsockopt(zmq.RCVTIMEO, timeout_ms)
|
183
|
+
self.socket.setsockopt(zmq.SNDTIMEO, timeout_ms)
|
184
|
+
self.socket.setsockopt(zmq.LINGER, 0)
|
185
|
+
return self.socket
|
186
|
+
|
187
|
+
def reload_certificates(self):
|
188
|
+
if self.public_keys_location and self.auth:
|
189
|
+
self.auth.configure_curve(domain='*',
|
190
|
+
location=self.public_keys_location)
|
191
|
+
|
192
|
+
def auto_reload_certificates(self):
|
193
|
+
self.observer = Observer()
|
194
|
+
self.observer.schedule(ReloadCertificatesHandler(self),
|
195
|
+
self.public_keys_location,
|
196
|
+
recursive=False)
|
197
|
+
self.observer.start()
|
198
|
+
|
199
|
+
def _close_socket(self) -> None:
|
200
|
+
"""
|
201
|
+
Closes the ZeroMQ socket and the context, and stops the authenticator
|
202
|
+
if it was started.
|
203
|
+
"""
|
204
|
+
if self._external_socket:
|
205
|
+
return
|
206
|
+
if self.observer:
|
207
|
+
self.observer.stop()
|
208
|
+
self.observer.join()
|
209
|
+
self.socket.close()
|
210
|
+
if self.auth:
|
211
|
+
self.auth.stop()
|
212
|
+
self.context.term()
|
213
|
+
|
214
|
+
def __enter__(self) -> zmq.Socket:
|
215
|
+
return self._create_socket(asyncio=False)
|
216
|
+
|
217
|
+
def __exit__(self, exc_type: Optional[type], exc_val: Optional[Exception],
|
218
|
+
exc_tb: Optional[type]) -> None:
|
219
|
+
self._close_socket()
|
220
|
+
|
221
|
+
async def __aenter__(self) -> zmq.Socket:
|
222
|
+
return self._create_socket(asyncio=True)
|
223
|
+
|
224
|
+
async def __aexit__(self, exc_type: Optional[type],
|
225
|
+
exc_val: Optional[Exception],
|
226
|
+
exc_tb: Optional[type]) -> None:
|
227
|
+
self._close_socket()
|
qulab/tools/__init__.py
ADDED
File without changes
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class _Missing():
|
2
|
+
|
3
|
+
def __repr__(self):
|
4
|
+
return "Missing"
|
5
|
+
|
6
|
+
|
7
|
+
Missing = _Missing()
|
8
|
+
|
9
|
+
|
10
|
+
def connect_trace(*mappings):
|
11
|
+
if not mappings:
|
12
|
+
return {}
|
13
|
+
result = {}
|
14
|
+
first_mapping = mappings[0]
|
15
|
+
for key in first_mapping:
|
16
|
+
current_value = key
|
17
|
+
trajectory = []
|
18
|
+
for mapping in mappings:
|
19
|
+
if current_value is Missing:
|
20
|
+
trajectory.append(Missing)
|
21
|
+
continue
|
22
|
+
if current_value in mapping:
|
23
|
+
next_value = mapping[current_value]
|
24
|
+
trajectory.append(next_value)
|
25
|
+
current_value = next_value
|
26
|
+
else:
|
27
|
+
trajectory.append(Missing)
|
28
|
+
current_value = Missing
|
29
|
+
result[key] = trajectory
|
30
|
+
return result
|
31
|
+
|
32
|
+
|
33
|
+
def connect(*mappings):
|
34
|
+
if not mappings:
|
35
|
+
return {}
|
36
|
+
return {
|
37
|
+
k: v[-1]
|
38
|
+
for k, v in connect_trace(*mappings).items() if v[-1] is not Missing
|
39
|
+
}
|
qulab/typing.py
ADDED
qulab/utils.py
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
import shlex
|
2
|
+
import subprocess
|
3
|
+
import sys
|
4
|
+
import time
|
5
|
+
|
6
|
+
import click
|
7
|
+
|
8
|
+
|
9
|
+
def run_detached(executable_path):
|
10
|
+
"""
|
11
|
+
启动可执行文件并完全分离(优先用 tmux/screen),无需额外终端窗口
|
12
|
+
支持 Windows、Linux 和 macOS
|
13
|
+
"""
|
14
|
+
try:
|
15
|
+
if sys.platform == 'win32' or not _unix_detach_with_tmux_or_screen(
|
16
|
+
executable_path):
|
17
|
+
# 回退到带终端窗口的方案
|
18
|
+
run_detached_with_terminal(executable_path)
|
19
|
+
|
20
|
+
except Exception as e:
|
21
|
+
click.echo(f"启动失败: {e}")
|
22
|
+
sys.exit(1)
|
23
|
+
|
24
|
+
|
25
|
+
def _windows_start(executable_path):
|
26
|
+
"""Windows 弹窗启动方案"""
|
27
|
+
subprocess.Popen(f'start cmd /k "{executable_path}"',
|
28
|
+
shell=True,
|
29
|
+
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
|
30
|
+
|
31
|
+
|
32
|
+
def _unix_detach_with_tmux_or_screen(executable_path):
|
33
|
+
"""Unix 后台分离方案(无窗口)"""
|
34
|
+
safe_path = shlex.quote(executable_path)
|
35
|
+
session_name = f"qulab_{int(time.time())}"
|
36
|
+
|
37
|
+
# 尝试 tmux
|
38
|
+
if _check_command_exists("tmux"):
|
39
|
+
command = [
|
40
|
+
"tmux",
|
41
|
+
"new-session",
|
42
|
+
"-d",
|
43
|
+
"-s",
|
44
|
+
session_name,
|
45
|
+
executable_path + " ; tmux wait-for -S finished", # 等待命令结束
|
46
|
+
";",
|
47
|
+
"tmux",
|
48
|
+
"wait-for",
|
49
|
+
"finished" # 防止进程立即退出
|
50
|
+
]
|
51
|
+
subprocess.Popen(" ".join(command), shell=True, start_new_session=True)
|
52
|
+
click.echo(f"已启动 tmux 会话: {session_name}")
|
53
|
+
click.echo(f"你可以使用 `tmux attach -t {session_name}` 来查看输出")
|
54
|
+
return True
|
55
|
+
|
56
|
+
# 尝试 screen
|
57
|
+
elif _check_command_exists("screen"):
|
58
|
+
command = ["screen", "-dmS", session_name, executable_path]
|
59
|
+
subprocess.Popen(command, start_new_session=True)
|
60
|
+
click.echo(f"已启动 screen 会话: {session_name}")
|
61
|
+
click.echo(f"你可以使用 `screen -r {session_name}` 来查看输出")
|
62
|
+
return True
|
63
|
+
|
64
|
+
return False
|
65
|
+
|
66
|
+
|
67
|
+
def run_detached_with_terminal(executable_path):
|
68
|
+
"""回退到带终端窗口的方案"""
|
69
|
+
if sys.platform == 'win32':
|
70
|
+
_windows_start(executable_path)
|
71
|
+
elif sys.platform == 'darwin':
|
72
|
+
script = f'tell app "Terminal" to do script "{executable_path}"'
|
73
|
+
subprocess.Popen(["osascript", "-e", script], start_new_session=True)
|
74
|
+
else:
|
75
|
+
try:
|
76
|
+
subprocess.Popen(
|
77
|
+
["gnome-terminal", "--", "sh", "-c", executable_path],
|
78
|
+
start_new_session=True)
|
79
|
+
except FileNotFoundError:
|
80
|
+
subprocess.Popen(["xterm", "-e", executable_path],
|
81
|
+
start_new_session=True)
|
82
|
+
|
83
|
+
|
84
|
+
def _check_command_exists(cmd):
|
85
|
+
"""检查命令行工具是否存在"""
|
86
|
+
try:
|
87
|
+
subprocess.check_output(["which", cmd], stderr=subprocess.DEVNULL)
|
88
|
+
return True
|
89
|
+
except:
|
90
|
+
return False
|
91
|
+
|
92
|
+
|
93
|
+
# 示例用法
|
94
|
+
if __name__ == '__main__':
|
95
|
+
run_detached("/path/to/your/program")
|
qulab/version.py
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "2.10.10"
|
@@ -0,0 +1,188 @@
|
|
1
|
+
import matplotlib.pyplot as plt
|
2
|
+
import numpy as np
|
3
|
+
|
4
|
+
from ._autoplot import autoplot
|
5
|
+
|
6
|
+
|
7
|
+
def plotLine(c0, c1, ax, **kwargs):
|
8
|
+
t = np.linspace(0, 1, 11)
|
9
|
+
c = (c1 - c0) * t + c0
|
10
|
+
ax.plot(c.real, c.imag, **kwargs)
|
11
|
+
|
12
|
+
|
13
|
+
def plotCircle(c0, r, ax, **kwargs):
|
14
|
+
t = np.linspace(0, 1, 1001) * 2 * np.pi
|
15
|
+
s = c0 + r * np.exp(1j * t)
|
16
|
+
ax.plot(s.real, s.imag, **kwargs)
|
17
|
+
|
18
|
+
|
19
|
+
def plotEllipse(c0, a, b, phi, ax, **kwargs):
|
20
|
+
t = np.linspace(0, 1, 1001) * 2 * np.pi
|
21
|
+
c = np.exp(1j * t)
|
22
|
+
s = c0 + (c.real * a + 1j * c.imag * b) * np.exp(1j * phi)
|
23
|
+
ax.plot(s.real, s.imag, **kwargs)
|
24
|
+
|
25
|
+
|
26
|
+
def plotDistribution(s0,
|
27
|
+
s1,
|
28
|
+
fig=None,
|
29
|
+
axes=None,
|
30
|
+
info=None,
|
31
|
+
hotThresh=10000,
|
32
|
+
logy=False):
|
33
|
+
from waveforms.math.fit import get_threshold_info, mult_gaussian_pdf
|
34
|
+
|
35
|
+
if info is None:
|
36
|
+
info = get_threshold_info(s0, s1)
|
37
|
+
else:
|
38
|
+
info = get_threshold_info(s0, s1, info['threshold'], info['phi'])
|
39
|
+
thr, phi = info['threshold'], info['phi']
|
40
|
+
# visibility, p0, p1 = info['visibility']
|
41
|
+
# print(
|
42
|
+
# f"thr={thr:.6f}, phi={phi:.6f}, visibility={visibility:.3f}, {p0}, {1-p1}"
|
43
|
+
# )
|
44
|
+
|
45
|
+
if axes is not None:
|
46
|
+
ax1, ax2 = axes
|
47
|
+
else:
|
48
|
+
if fig is None:
|
49
|
+
fig = plt.figure()
|
50
|
+
ax1 = fig.add_subplot(121)
|
51
|
+
ax2 = fig.add_subplot(122)
|
52
|
+
|
53
|
+
if (len(s0) + len(s1)) < hotThresh:
|
54
|
+
ax1.plot(np.real(s0), np.imag(s0), '.', alpha=0.2)
|
55
|
+
ax1.plot(np.real(s1), np.imag(s1), '.', alpha=0.2)
|
56
|
+
else:
|
57
|
+
_, *bins = np.histogram2d(np.real(np.hstack([s0, s1])),
|
58
|
+
np.imag(np.hstack([s0, s1])),
|
59
|
+
bins=50)
|
60
|
+
|
61
|
+
H0, *_ = np.histogram2d(np.real(s0),
|
62
|
+
np.imag(s0),
|
63
|
+
bins=bins,
|
64
|
+
density=True)
|
65
|
+
H1, *_ = np.histogram2d(np.real(s1),
|
66
|
+
np.imag(s1),
|
67
|
+
bins=bins,
|
68
|
+
density=True)
|
69
|
+
vlim = max(np.max(np.abs(H0)), np.max(np.abs(H1)))
|
70
|
+
|
71
|
+
ax1.imshow(H1.T - H0.T,
|
72
|
+
alpha=(np.fmax(H0.T, H1.T) / vlim).clip(0, 1),
|
73
|
+
interpolation='nearest',
|
74
|
+
origin='lower',
|
75
|
+
cmap='coolwarm',
|
76
|
+
vmin=-vlim,
|
77
|
+
vmax=vlim,
|
78
|
+
extent=(bins[0][0], bins[0][-1], bins[1][0], bins[1][-1]))
|
79
|
+
|
80
|
+
ax1.axis('equal')
|
81
|
+
ax1.set_xticks([])
|
82
|
+
ax1.set_yticks([])
|
83
|
+
for s in ax1.spines.values():
|
84
|
+
s.set_visible(False)
|
85
|
+
|
86
|
+
# c0, c1 = info['center']
|
87
|
+
# a0, b0, a1, b1 = info['std']
|
88
|
+
params = info['params']
|
89
|
+
r0, i0, r1, i1 = params[0][0], params[1][0], params[0][1], params[1][1]
|
90
|
+
a0, b0, a1, b1 = params[0][2], params[1][2], params[0][3], params[1][3]
|
91
|
+
c0 = (r0 + 1j * i0) * np.exp(1j * phi)
|
92
|
+
c1 = (r1 + 1j * i1) * np.exp(1j * phi)
|
93
|
+
phi0 = phi + params[0][6]
|
94
|
+
phi1 = phi + params[1][6]
|
95
|
+
plotEllipse(c0, 2 * a0, 2 * b0, phi0, ax1)
|
96
|
+
plotEllipse(c1, 2 * a1, 2 * b1, phi1, ax1)
|
97
|
+
|
98
|
+
im0, im1 = info['idle']
|
99
|
+
lim = min(im0.min(), im1.min()), max(im0.max(), im1.max())
|
100
|
+
t = (np.linspace(lim[0], lim[1], 3) + 1j * thr) * np.exp(-1j * phi)
|
101
|
+
ax1.plot(t.imag, t.real, 'k--')
|
102
|
+
|
103
|
+
ax1.plot(np.real(c0), np.imag(c0), 'o', color='C3')
|
104
|
+
ax1.plot(np.real(c1), np.imag(c1), 'o', color='C4')
|
105
|
+
|
106
|
+
re0, re1 = info['signal']
|
107
|
+
x, a, b, c = info['cdf']
|
108
|
+
|
109
|
+
xrange = (min(re0.min(), re1.min()), max(re0.max(), re1.max()))
|
110
|
+
|
111
|
+
n0, bins0, *_ = ax2.hist(re0, bins=80, range=xrange, alpha=0.5)
|
112
|
+
n1, bins1, *_ = ax2.hist(re1, bins=80, range=xrange, alpha=0.5)
|
113
|
+
|
114
|
+
x_range = np.linspace(x.min(), x.max(), 1001)
|
115
|
+
*_, cov0, cov1 = info['std']
|
116
|
+
ax2.plot(
|
117
|
+
x_range,
|
118
|
+
np.sum(n0) * (bins0[1] - bins0[0]) *
|
119
|
+
mult_gaussian_pdf(x_range, [r0, r1], [
|
120
|
+
np.sqrt(cov0[0, 0]), np.sqrt(cov1[0, 0])
|
121
|
+
], [params[0][4], 1 - params[0][4]]))
|
122
|
+
ax2.plot(
|
123
|
+
x_range,
|
124
|
+
np.sum(n1) * (bins1[1] - bins1[0]) *
|
125
|
+
mult_gaussian_pdf(x_range, [r0, r1], [
|
126
|
+
np.sqrt(cov0[0, 0]), np.sqrt(cov1[0, 0])
|
127
|
+
], [params[0][5], 1 - params[0][5]]))
|
128
|
+
ax2.set_ylabel('Count')
|
129
|
+
ax2.set_xlabel('Projection Axes')
|
130
|
+
if logy:
|
131
|
+
ax2.set_yscale('log')
|
132
|
+
ax2.set_ylim(0.1, max(np.sum(n0), np.sum(n1)))
|
133
|
+
|
134
|
+
ax3 = ax2.twinx()
|
135
|
+
ax3.plot(x, a, '--', lw=1, color='C0')
|
136
|
+
ax3.plot(x, b, '--', lw=1, color='C1')
|
137
|
+
ax3.plot(x, c, 'k--', alpha=0.5, lw=1)
|
138
|
+
ax3.set_ylim(0, 1.1)
|
139
|
+
ax3.vlines(thr, 0, 1.1, 'k', alpha=0.5)
|
140
|
+
ax3.set_ylabel('Integral Probability')
|
141
|
+
|
142
|
+
return info
|
143
|
+
|
144
|
+
|
145
|
+
ALLXYSeq = [('I', 'I'), ('X', 'X'), ('Y', 'Y'), ('X', 'Y'), ('Y', 'X'),
|
146
|
+
('X/2', 'I'), ('Y/2', 'I'), ('X/2', 'Y/2'), ('Y/2', 'X/2'),
|
147
|
+
('X/2', 'Y'), ('Y/2', 'X'), ('X', 'Y/2'), ('Y', 'X/2'),
|
148
|
+
('X/2', 'X'), ('X', 'X/2'), ('Y/2', 'Y'), ('Y', 'Y/2'), ('X', 'I'),
|
149
|
+
('Y', 'I'), ('X/2', 'X/2'), ('Y/2', 'Y/2')]
|
150
|
+
|
151
|
+
|
152
|
+
def plotALLXY(data, ax=None):
|
153
|
+
assert len(data) % len(ALLXYSeq) == 0
|
154
|
+
|
155
|
+
if ax is None:
|
156
|
+
ax = plt.gca()
|
157
|
+
|
158
|
+
ax.plot(np.array(data), 'o-')
|
159
|
+
repeat = len(data) // len(ALLXYSeq)
|
160
|
+
ax.set_xticks(np.arange(len(ALLXYSeq)) * repeat + 0.5 * (repeat - 1))
|
161
|
+
ax.set_xticklabels([','.join(seq) for seq in ALLXYSeq], rotation=60)
|
162
|
+
ax.grid(which='major')
|
163
|
+
|
164
|
+
|
165
|
+
def plot_mat(rho, title='$\\chi$', cmap='coolwarm'):
|
166
|
+
lim = np.abs(rho).max()
|
167
|
+
N = rho.shape[0]
|
168
|
+
|
169
|
+
fig = plt.figure(figsize=(6, 4))
|
170
|
+
fig.suptitle(title)
|
171
|
+
|
172
|
+
ax1 = plt.subplot(121)
|
173
|
+
cax1 = ax1.imshow(rho.real, vmin=-lim, vmax=lim, cmap=cmap)
|
174
|
+
ax1.set_title('Re')
|
175
|
+
ax1.set_xticks(np.arange(N))
|
176
|
+
ax1.set_yticks(np.arange(N))
|
177
|
+
|
178
|
+
ax2 = plt.subplot(122)
|
179
|
+
cax2 = ax2.imshow(rho.imag, vmin=-lim, vmax=lim, cmap=cmap)
|
180
|
+
ax2.set_title('Im')
|
181
|
+
ax2.set_xticks(np.arange(N))
|
182
|
+
ax2.set_yticks(np.arange(N))
|
183
|
+
|
184
|
+
plt.subplots_adjust(bottom=0.2, right=0.9, top=0.95)
|
185
|
+
|
186
|
+
cbar_ax = fig.add_axes([0.15, 0.15, 0.7, 0.05])
|
187
|
+
cb = fig.colorbar(cax1, cax=cbar_ax, orientation='horizontal')
|
188
|
+
plt.show()
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import pathlib
|
2
|
+
import pickle
|
3
|
+
|
4
|
+
import click
|
5
|
+
import dill
|
6
|
+
import matplotlib.pyplot as plt
|
7
|
+
import numpy as np
|
8
|
+
|
9
|
+
from .qdat import draw as draw_qdat
|
10
|
+
|
11
|
+
default_draw_methods = {
|
12
|
+
'.qdat': draw_qdat,
|
13
|
+
}
|
14
|
+
|
15
|
+
|
16
|
+
def load_data(fname):
|
17
|
+
try:
|
18
|
+
from home.hkxu.tools import get_record_by_id
|
19
|
+
record_id = int(str(fname))
|
20
|
+
return get_record_by_id(record_id).data
|
21
|
+
except:
|
22
|
+
pass
|
23
|
+
with open(fname, 'rb') as f:
|
24
|
+
try:
|
25
|
+
data = pickle.load(f)
|
26
|
+
except:
|
27
|
+
f.seek(0)
|
28
|
+
data = dill.load(f)
|
29
|
+
return data
|
30
|
+
|
31
|
+
|
32
|
+
def draw_common(data):
|
33
|
+
try:
|
34
|
+
script = data['meta']['plot_script']
|
35
|
+
assert script.strip()
|
36
|
+
global_namespace = {'plt': plt, 'np': np, 'result': data}
|
37
|
+
exec(script, global_namespace)
|
38
|
+
except:
|
39
|
+
from home.hkxu.tools import plot_record
|
40
|
+
plot_record(data['meta']['id'])
|
41
|
+
|
42
|
+
|
43
|
+
def draw_error(data, text="No validate plot script found"):
|
44
|
+
fig = plt.figure()
|
45
|
+
ax = fig.add_subplot(111)
|
46
|
+
ax.text(0.5, 0.5, text, ha='center', va='center')
|
47
|
+
ax.set_axis_off()
|
48
|
+
return fig
|
49
|
+
|
50
|
+
|
51
|
+
@click.command()
|
52
|
+
@click.argument('fname', default='')
|
53
|
+
def plot(fname):
|
54
|
+
"""Plot the data in the file."""
|
55
|
+
try:
|
56
|
+
fname = pathlib.Path(fname)
|
57
|
+
data = load_data(fname)
|
58
|
+
try:
|
59
|
+
draw_common(data)
|
60
|
+
except:
|
61
|
+
default_draw_methods.get(fname.suffix, draw_error)(data)
|
62
|
+
except FileNotFoundError:
|
63
|
+
draw_error(None, text=f"File {fname} not found.")
|
64
|
+
except pickle.UnpicklingError:
|
65
|
+
draw_error(None, text=f"File {fname} is not a pickle file.")
|
66
|
+
|
67
|
+
plt.show()
|
68
|
+
|
69
|
+
|
70
|
+
if __name__ == '__main__':
|
71
|
+
plot()
|