QuLab 2.3.6__cp310-cp310-macosx_10_9_universal2.whl → 2.4.0__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-2.3.6.dist-info → QuLab-2.4.0.dist-info}/METADATA +21 -18
- {QuLab-2.3.6.dist-info → QuLab-2.4.0.dist-info}/RECORD +28 -17
- {QuLab-2.3.6.dist-info → QuLab-2.4.0.dist-info}/WHEEL +1 -1
- qulab/__main__.py +11 -7
- qulab/dicttree.py +511 -0
- qulab/executor/__init__.py +5 -0
- qulab/executor/__main__.py +89 -0
- qulab/executor/load.py +202 -0
- qulab/executor/schedule.py +223 -0
- qulab/executor/storage.py +143 -0
- qulab/executor/transform.py +90 -0
- qulab/executor/utils.py +107 -0
- qulab/fun.cpython-310-darwin.so +0 -0
- qulab/scan/curd.py +1 -1
- qulab/scan/expression.py +4 -0
- qulab/scan/models.py +10 -11
- qulab/scan/server.py +4 -1
- qulab/sys/device/basedevice.py +8 -0
- qulab/sys/device/utils.py +46 -13
- qulab/sys/rpc/router.py +35 -0
- qulab/version.py +1 -1
- qulab/visualization/_autoplot.py +4 -3
- qulab/visualization/plot_circ.py +319 -0
- qulab/visualization/plot_seq.py +152 -0
- qulab/visualization/rot3d.py +23 -0
- {QuLab-2.3.6.dist-info → QuLab-2.4.0.dist-info}/LICENSE +0 -0
- {QuLab-2.3.6.dist-info → QuLab-2.4.0.dist-info}/entry_points.txt +0 -0
- {QuLab-2.3.6.dist-info → QuLab-2.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import loguru
|
|
2
|
+
|
|
3
|
+
from .storage import Result
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _query_config(name: str, default=None):
|
|
7
|
+
import pickle
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
with open('parameters.pkl', 'rb') as f:
|
|
11
|
+
parameters = pickle.load(f)
|
|
12
|
+
except:
|
|
13
|
+
parameters = {}
|
|
14
|
+
|
|
15
|
+
return parameters.get(name, default)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _update_config(updates):
|
|
19
|
+
import pickle
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
with open('parameters.pkl', 'rb') as f:
|
|
23
|
+
parameters = pickle.load(f)
|
|
24
|
+
except:
|
|
25
|
+
parameters = {}
|
|
26
|
+
|
|
27
|
+
for k, v in updates.items():
|
|
28
|
+
parameters[k] = v
|
|
29
|
+
|
|
30
|
+
with open('parameters.pkl', 'wb') as f:
|
|
31
|
+
pickle.dump(parameters, f)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def update_parameters(result: Result):
|
|
35
|
+
update_config(result.params)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def result_to_params(result: Result | None) -> tuple | None:
|
|
39
|
+
if result is None:
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
state = 'Bad data'
|
|
43
|
+
match (result.in_spec, result.bad_data):
|
|
44
|
+
case (True, False):
|
|
45
|
+
state = 'In spec'
|
|
46
|
+
case (False, True):
|
|
47
|
+
state = 'Bad data'
|
|
48
|
+
case (False, False):
|
|
49
|
+
state = 'Out of spec'
|
|
50
|
+
|
|
51
|
+
return state, result.params, result.info
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def params_to_result(params: tuple) -> Result:
|
|
55
|
+
state, cali, info = params
|
|
56
|
+
result = Result()
|
|
57
|
+
if state in ['In spec', 'OK']:
|
|
58
|
+
result.in_spec = True
|
|
59
|
+
result.bad_data = False
|
|
60
|
+
elif state == ['Bad data', 'Bad']:
|
|
61
|
+
result.bad_data = True
|
|
62
|
+
result.in_spec = False
|
|
63
|
+
else:
|
|
64
|
+
result.bad_data = False
|
|
65
|
+
result.in_spec = False
|
|
66
|
+
result.params = cali
|
|
67
|
+
result.info = info
|
|
68
|
+
return result
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
query_config = _query_config
|
|
72
|
+
update_config = _update_config
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def set_config_api(query_method, update_method):
|
|
76
|
+
"""
|
|
77
|
+
Set the query and update methods for the config.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
query_method: The query method.
|
|
81
|
+
the method should take a key and return the value.
|
|
82
|
+
update_method: The update method.
|
|
83
|
+
the method should take a dict of updates.
|
|
84
|
+
"""
|
|
85
|
+
global query_config, update_config
|
|
86
|
+
|
|
87
|
+
query_config = query_method
|
|
88
|
+
update_config = update_method
|
|
89
|
+
|
|
90
|
+
return query_config, update_config
|
qulab/executor/utils.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from .load import load_workflow
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Node:
|
|
7
|
+
|
|
8
|
+
def __init__(self, name: str):
|
|
9
|
+
self.name = name
|
|
10
|
+
self.dependents = []
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Tree:
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.nodes = {}
|
|
17
|
+
self.heads = []
|
|
18
|
+
|
|
19
|
+
def add_node(self, node: str):
|
|
20
|
+
self.nodes[node] = Node(node)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def dependent_tree(node: str, code_path: str | Path) -> dict[str, list[str]]:
|
|
24
|
+
'''
|
|
25
|
+
Returns a dict of nodes and their dependents.
|
|
26
|
+
'''
|
|
27
|
+
tree = {}
|
|
28
|
+
for n in load_workflow(node, code_path).depends()[0]:
|
|
29
|
+
tree[n] = dependent_tree(n, code_path)
|
|
30
|
+
return tree
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def workflow_template(deps: list[str]) -> str:
|
|
34
|
+
return f"""
|
|
35
|
+
from loguru import logger
|
|
36
|
+
|
|
37
|
+
import numpy as np
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# 多长时间应该检查一次校准实验,单位是秒。
|
|
41
|
+
__timeout__ = 200
|
|
42
|
+
|
|
43
|
+
def depends():
|
|
44
|
+
return [{deps!r}]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def calibrate():
|
|
48
|
+
logger.info(f"run {{__name__}}")
|
|
49
|
+
|
|
50
|
+
# calibrate 是一个完整的校准实验,如power Rabi,Ramsey等。
|
|
51
|
+
# 你需要足够的扫描点,以使得后续的 analyze 可以拟合出合适的参数。
|
|
52
|
+
|
|
53
|
+
# 这里只是一个示例,实际上你需要在这里写上你的校准代码。
|
|
54
|
+
x = np.linspace(0, 2*np.pi, 101)
|
|
55
|
+
y = []
|
|
56
|
+
for i in x:
|
|
57
|
+
y.append(np.sin(i))
|
|
58
|
+
|
|
59
|
+
return x, y
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def analyze(*args, history):
|
|
63
|
+
import random
|
|
64
|
+
|
|
65
|
+
# 完整校准后的状态有两种:OK 和 Bad,分别对应校准成功和校准失败。
|
|
66
|
+
# 校准失败是指出现坏数据,无法简单通过重新运行本次校准解决,需要
|
|
67
|
+
# 检查前置步骤。
|
|
68
|
+
state = random.choice(['OK', 'Bad'])
|
|
69
|
+
|
|
70
|
+
# 参数是一个字典,包含了本次校准得到的参数,后续会更新到config表中。
|
|
71
|
+
parameters = {{'gate.R.Q1.params.amp':1}}
|
|
72
|
+
|
|
73
|
+
# 其他信息可以是任何可序列化的内容,你可以将你想要记录的信息放在这里。
|
|
74
|
+
# 下次校准分析时,这些信息也会在 history 参数中一起传入,帮助你在下
|
|
75
|
+
# 次分析时对比参考。
|
|
76
|
+
other_infomation = {{}}
|
|
77
|
+
|
|
78
|
+
return state, parameters, other_infomation
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def check():
|
|
82
|
+
logger.info(f"check {{__name__}}")
|
|
83
|
+
|
|
84
|
+
# check 是一个快速检查实验,用于检查校准是否过时。
|
|
85
|
+
# 你只需要少数扫描点,让后续的 check_analyze 知道参数是否漂移,数据
|
|
86
|
+
# 坏没坏就够了,不要求拟合。
|
|
87
|
+
|
|
88
|
+
# 这里只是一个示例,实际上你需要在这里写上你的检查代码。
|
|
89
|
+
x = np.linspace(0, 2*np.pi, 5)
|
|
90
|
+
y = []
|
|
91
|
+
for i in x:
|
|
92
|
+
y.append(np.sin(i))
|
|
93
|
+
|
|
94
|
+
return x, y
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def check_analyze(*args, history):
|
|
98
|
+
import random
|
|
99
|
+
|
|
100
|
+
# 状态有三种:Outdated, OK, Bad,分别对应过时、正常、坏数据。
|
|
101
|
+
# Outdated 是指数据过时,即参数漂了,需要重新校准。
|
|
102
|
+
# OK 是指数据正常,参数也没漂,不用重新校准。
|
|
103
|
+
# Bad 是指数据坏了,无法校准,需要检查前置步骤。
|
|
104
|
+
state = random.choice(['Outdated', 'OK', 'Bad'])
|
|
105
|
+
|
|
106
|
+
return state, {{}}, {{}}
|
|
107
|
+
"""
|
qulab/fun.cpython-310-darwin.so
CHANGED
|
Binary file
|
qulab/scan/curd.py
CHANGED
|
@@ -7,7 +7,7 @@ from typing import Sequence, Type, Union
|
|
|
7
7
|
from sqlalchemy.orm import Query, Session, aliased
|
|
8
8
|
from sqlalchemy.orm.exc import NoResultFound
|
|
9
9
|
from sqlalchemy.orm.session import Session
|
|
10
|
-
from
|
|
10
|
+
from ..dicttree import foldDict
|
|
11
11
|
|
|
12
12
|
from .models import (Cell, Comment, Config, InputText, Notebook, Record,
|
|
13
13
|
Report, Sample, Tag, utcnow)
|
qulab/scan/expression.py
CHANGED
qulab/scan/models.py
CHANGED
|
@@ -8,7 +8,6 @@ from sqlalchemy import (Column, DateTime, Float, ForeignKey, Integer,
|
|
|
8
8
|
from sqlalchemy.orm import (backref, declarative_base, relationship,
|
|
9
9
|
sessionmaker)
|
|
10
10
|
from sqlalchemy.orm.session import Session
|
|
11
|
-
from waveforms.security import InvalidKey, encryptPassword, verifyPassword
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
def utcnow():
|
|
@@ -107,15 +106,15 @@ class User(Base):
|
|
|
107
106
|
attachments = relationship('Attachment', back_populates='user')
|
|
108
107
|
comments = relationship('Comment', back_populates='user')
|
|
109
108
|
|
|
110
|
-
def setPassword(self, password):
|
|
111
|
-
|
|
109
|
+
# def setPassword(self, password):
|
|
110
|
+
# self.hashed_password = encryptPassword(password)
|
|
112
111
|
|
|
113
|
-
def verify(self, password):
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
# def verify(self, password):
|
|
113
|
+
# try:
|
|
114
|
+
# verifyPassword(password, self.hashed_password)
|
|
115
|
+
# return True
|
|
116
|
+
# except InvalidKey:
|
|
117
|
+
# return False
|
|
119
118
|
|
|
120
119
|
def __repr__(self):
|
|
121
120
|
return f"User(name='{self.name}')"
|
|
@@ -526,13 +525,13 @@ def create_tables(engine, tables_only=False):
|
|
|
526
525
|
root_role = Role(name='root')
|
|
527
526
|
admin_role = Role(name='admin')
|
|
528
527
|
root_user = User(name='root')
|
|
529
|
-
root_user.setPassword('123')
|
|
528
|
+
# root_user.setPassword('123')
|
|
530
529
|
root_user.roles.append(root_role)
|
|
531
530
|
root_user.roles.append(admin_role)
|
|
532
531
|
|
|
533
532
|
guest_role = Role(name='guest')
|
|
534
533
|
guest_user = User(name='guest')
|
|
535
|
-
guest_user.setPassword('')
|
|
534
|
+
# guest_user.setPassword('')
|
|
536
535
|
guest_user.roles.append(guest_role)
|
|
537
536
|
|
|
538
537
|
t1 = SampleAccountType(name='factory')
|
qulab/scan/server.py
CHANGED
|
@@ -276,7 +276,10 @@ async def handle(session: Session, request: Request, datapath: Path):
|
|
|
276
276
|
case 'notebook_extend':
|
|
277
277
|
notebook = session.get(Notebook, msg['notebook_id'])
|
|
278
278
|
inputCells = msg.get('input_cells', [""])
|
|
279
|
-
|
|
279
|
+
try:
|
|
280
|
+
aready_saved = len(notebook.cells)
|
|
281
|
+
except:
|
|
282
|
+
aready_saved = 0
|
|
280
283
|
if len(inputCells) > aready_saved:
|
|
281
284
|
for cell in inputCells[aready_saved:]:
|
|
282
285
|
cell = create_cell(session, notebook, cell)
|
qulab/sys/device/basedevice.py
CHANGED
qulab/sys/device/utils.py
CHANGED
|
@@ -2,6 +2,20 @@ import struct
|
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
|
|
5
|
+
types = {
|
|
6
|
+
"b" : ( int, 'b'), "B" : ( int, 'B'),
|
|
7
|
+
"h" : ( int, 'h'), "H" : ( int, 'H'),
|
|
8
|
+
"i" : ( int, 'i'), "I" : ( int, 'I'),
|
|
9
|
+
"q" : ( int, 'q'), "Q" : ( int, 'Q'),
|
|
10
|
+
"f" : (float, 'f'), "d" : (float, 'd'),
|
|
11
|
+
"int8" : ( int, 'b'), "uint8" : ( int, 'B'),
|
|
12
|
+
"int16" : ( int, 'h'), "uint16" : ( int, 'H'),
|
|
13
|
+
"int32" : ( int, 'i'), "uint32" : ( int, 'I'),
|
|
14
|
+
"int64" : ( int, 'q'), "uint64" : ( int, 'Q'),
|
|
15
|
+
"float" : (float, 'f'), "double" : (float, 'd'),
|
|
16
|
+
"float32": (float, 'f'), "float64": (float, 'd')
|
|
17
|
+
} # yapf: disable
|
|
18
|
+
|
|
5
19
|
|
|
6
20
|
def IEEE_488_2_BinBlock(datalist, dtype="int16", is_big_endian=True):
|
|
7
21
|
"""
|
|
@@ -16,19 +30,6 @@ def IEEE_488_2_BinBlock(datalist, dtype="int16", is_big_endian=True):
|
|
|
16
30
|
binblock, header
|
|
17
31
|
二进制块, 以及其 'header'
|
|
18
32
|
"""
|
|
19
|
-
types = {"b" : ( int, 'b'), "B" : ( int, 'B'),
|
|
20
|
-
"h" : ( int, 'h'), "H" : ( int, 'H'),
|
|
21
|
-
"i" : ( int, 'i'), "I" : ( int, 'I'),
|
|
22
|
-
"q" : ( int, 'q'), "Q" : ( int, 'Q'),
|
|
23
|
-
"f" : (float, 'f'), "d" : (float, 'd'),
|
|
24
|
-
"int8" : ( int, 'b'), "uint8" : ( int, 'B'),
|
|
25
|
-
"int16" : ( int, 'h'), "uint16" : ( int, 'H'),
|
|
26
|
-
"int32" : ( int, 'i'), "uint32" : ( int, 'I'),
|
|
27
|
-
"int64" : ( int, 'q'), "uint64" : ( int, 'Q'),
|
|
28
|
-
"float" : (float, 'f'), "double" : (float, 'd'),
|
|
29
|
-
"float32": (float, 'f'), "float64": (float, 'd')
|
|
30
|
-
} # yapf: disable
|
|
31
|
-
|
|
32
33
|
if isinstance(datalist, bytes):
|
|
33
34
|
datablock = datalist
|
|
34
35
|
else:
|
|
@@ -44,3 +45,35 @@ def IEEE_488_2_BinBlock(datalist, dtype="int16", is_big_endian=True):
|
|
|
44
45
|
header = '#%d%s' % (len(size), size)
|
|
45
46
|
|
|
46
47
|
return header.encode() + datablock
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def decode_IEEE_488_2_BinBlock(data, dtype="int16", is_big_endian=True):
|
|
51
|
+
"""
|
|
52
|
+
解析 IEEE 488.2 标准二进制块
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
data : 二进制块
|
|
56
|
+
dtype : 数据类型
|
|
57
|
+
endian : 字节序
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
datalist
|
|
61
|
+
"""
|
|
62
|
+
if isinstance(data, bytes):
|
|
63
|
+
header = data[:2]
|
|
64
|
+
size = int(data[2:2 + int(header[1:2])])
|
|
65
|
+
assert size == len(data) - 2 - int(header[1:2]), "data size error"
|
|
66
|
+
datablock = data[2 + int(header[1:2]):size + 2 + int(header[1:2])]
|
|
67
|
+
else:
|
|
68
|
+
raise ValueError("data must be bytes")
|
|
69
|
+
|
|
70
|
+
if is_big_endian:
|
|
71
|
+
endianc = '>'
|
|
72
|
+
else:
|
|
73
|
+
endianc = '<'
|
|
74
|
+
datalist = list(
|
|
75
|
+
struct.unpack(
|
|
76
|
+
'%s%d%s' % (endianc, size // struct.calcsize(types[dtype][1]),
|
|
77
|
+
types[dtype][1]), datablock))
|
|
78
|
+
|
|
79
|
+
return datalist
|
qulab/sys/rpc/router.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import random
|
|
3
|
+
|
|
4
|
+
import zmq
|
|
5
|
+
import zmq.asyncio
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def handle_client(socket, identity, message):
|
|
9
|
+
print(f"Received request from {identity}: {message.decode()}")
|
|
10
|
+
# 随机延时 0 到 3 秒
|
|
11
|
+
await asyncio.sleep(random.uniform(0, 3))
|
|
12
|
+
task_id = random.randint(1000, 9999) # 随机生成一个任务 ID
|
|
13
|
+
await socket.send_multipart([identity, f"Task ID: {task_id}".encode()])
|
|
14
|
+
print(f"Sent Task ID {task_id} to {identity}")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def server():
|
|
18
|
+
context = zmq.asyncio.Context()
|
|
19
|
+
socket = context.socket(zmq.ROUTER)
|
|
20
|
+
socket.bind("tcp://*:5555")
|
|
21
|
+
|
|
22
|
+
while True:
|
|
23
|
+
try:
|
|
24
|
+
identity, message = await socket.recv_multipart()
|
|
25
|
+
asyncio.create_task(handle_client(socket, identity, message))
|
|
26
|
+
except Exception as e:
|
|
27
|
+
print(f"An error occurred: {e}")
|
|
28
|
+
break
|
|
29
|
+
|
|
30
|
+
socket.close()
|
|
31
|
+
context.term()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
asyncio.run(server())
|
qulab/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.
|
|
1
|
+
__version__ = "2.4.0"
|
qulab/visualization/_autoplot.py
CHANGED
|
@@ -239,8 +239,8 @@ def plot_img(x,
|
|
|
239
239
|
kwds.setdefault('aspect', 'auto')
|
|
240
240
|
kwds.setdefault('interpolation', 'nearest')
|
|
241
241
|
|
|
242
|
-
vmin = kwds.
|
|
243
|
-
vmax = kwds.
|
|
242
|
+
vmin = kwds.pop('vmin', np.nanmin(z))
|
|
243
|
+
vmax = kwds.pop('vmax', np.nanmax(z))
|
|
244
244
|
if zscale == 'log':
|
|
245
245
|
kwds.setdefault('norm', LogNorm(vmax=vmax, vmin=vmin))
|
|
246
246
|
elif zscale == 'symlog':
|
|
@@ -262,7 +262,8 @@ def plot_img(x,
|
|
|
262
262
|
kwds.pop('origin', None)
|
|
263
263
|
kwds.pop('aspect', None)
|
|
264
264
|
kwds.pop('interpolation', None)
|
|
265
|
-
|
|
265
|
+
shading = kwds.pop('shading', 'nearest')
|
|
266
|
+
pc = ax.pcolormesh(x, y, z, shading=shading, **kwds)
|
|
266
267
|
xlabel = f"{xlabel} [{x_unit}]" if x_unit else xlabel
|
|
267
268
|
ylabel = f"{ylabel} [{y_unit}]" if y_unit else ylabel
|
|
268
269
|
ax.set_xlabel(xlabel)
|