qubitclient 0.1.4__py3-none-any.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.
- qubitclient/__init__.py +5 -0
- qubitclient/draw/__init__.py +0 -0
- qubitclient/draw/optpipulsepltplotter.py +75 -0
- qubitclient/draw/optpipulseplyplotter.py +114 -0
- qubitclient/draw/pltmanager.py +50 -0
- qubitclient/draw/pltplotter.py +20 -0
- qubitclient/draw/plymanager.py +57 -0
- qubitclient/draw/plyplotter.py +21 -0
- qubitclient/draw/powershiftpltplotter.py +108 -0
- qubitclient/draw/powershiftplyplotter.py +194 -0
- qubitclient/draw/rabicospltplotter.py +74 -0
- qubitclient/draw/rabicosplyplotter.py +90 -0
- qubitclient/draw/rabipltplotter.py +66 -0
- qubitclient/draw/rabiplyplotter.py +86 -0
- qubitclient/draw/s21peakpltplotter.py +67 -0
- qubitclient/draw/s21peakplyplotter.py +124 -0
- qubitclient/draw/s21vfluxpltplotter.py +84 -0
- qubitclient/draw/s21vfluxplyplotter.py +163 -0
- qubitclient/draw/singleshotpltplotter.py +149 -0
- qubitclient/draw/singleshotplyplotter.py +324 -0
- qubitclient/draw/spectrum2dpltplotter.py +107 -0
- qubitclient/draw/spectrum2dplyplotter.py +244 -0
- qubitclient/draw/spectrum2dscopepltplotter.py +72 -0
- qubitclient/draw/spectrum2dscopeplyplotter.py +195 -0
- qubitclient/draw/spectrumpltplotter.py +106 -0
- qubitclient/draw/spectrumplyplotter.py +133 -0
- qubitclient/draw/t1fitpltplotter.py +76 -0
- qubitclient/draw/t1fitplyplotter.py +109 -0
- qubitclient/draw/t2fitpltplotter.py +70 -0
- qubitclient/draw/t2fitplyplotter.py +111 -0
- qubitclient/nnscope/nnscope.py +51 -0
- qubitclient/nnscope/nnscope_api/curve/__init__.py +0 -0
- qubitclient/nnscope/nnscope_api/curve/curve_type.py +15 -0
- qubitclient/nnscope/task.py +170 -0
- qubitclient/nnscope/utils/data_convert.py +114 -0
- qubitclient/nnscope/utils/data_parser.py +41 -0
- qubitclient/nnscope/utils/request_tool.py +41 -0
- qubitclient/nnscope/utils/result_parser.py +55 -0
- qubitclient/scope/scope.py +50 -0
- qubitclient/scope/scope_api/__init__.py +8 -0
- qubitclient/scope/scope_api/api/__init__.py +1 -0
- qubitclient/scope/scope_api/api/defined_tasks/__init__.py +1 -0
- qubitclient/scope/scope_api/api/defined_tasks/get_task_result_api_v1_tasks_demo_pk_get.py +155 -0
- qubitclient/scope/scope_api/api/defined_tasks/get_task_result_api_v1_tasks_scope_pk_get.py +155 -0
- qubitclient/scope/scope_api/api/defined_tasks/optpipulse_api_v1_tasks_scope_optpipulse_post.py +218 -0
- qubitclient/scope/scope_api/api/defined_tasks/powershift_api_v1_tasks_scope_powershift_post.py +218 -0
- qubitclient/scope/scope_api/api/defined_tasks/rabi_api_v1_tasks_scope_rabi_post.py +218 -0
- qubitclient/scope/scope_api/api/defined_tasks/rabicos_api_v1_tasks_scope_rabicospeak_post.py +218 -0
- qubitclient/scope/scope_api/api/defined_tasks/s21peak_api_v1_tasks_scope_s21peak_post.py +218 -0
- qubitclient/scope/scope_api/api/defined_tasks/s21vflux_api_v1_tasks_scope_s21vflux_post.py +218 -0
- qubitclient/scope/scope_api/api/defined_tasks/singleshot_api_v1_tasks_scope_singleshot_post.py +218 -0
- qubitclient/scope/scope_api/api/defined_tasks/spectrum2d_api_v1_tasks_scope_spectrum2d_post.py +218 -0
- qubitclient/scope/scope_api/api/defined_tasks/spectrum_api_v1_tasks_scope_spectrum_post.py +218 -0
- qubitclient/scope/scope_api/api/defined_tasks/t1fit_api_v1_tasks_scope_t1fit_post.py +218 -0
- qubitclient/scope/scope_api/api/defined_tasks/t1fit_api_v1_tasks_scope_t2fit_post.py +218 -0
- qubitclient/scope/scope_api/client.py +268 -0
- qubitclient/scope/scope_api/errors.py +16 -0
- qubitclient/scope/scope_api/models/__init__.py +31 -0
- qubitclient/scope/scope_api/models/body_optpipulse_api_v1_tasks_scope_optpipulse_post.py +83 -0
- qubitclient/scope/scope_api/models/body_powershift_api_v1_tasks_scope_powershift_post.py +83 -0
- qubitclient/scope/scope_api/models/body_rabi_api_v1_tasks_scope_rabi_post.py +83 -0
- qubitclient/scope/scope_api/models/body_rabicos_api_v1_tasks_scope_rabicospeak_post.py +83 -0
- qubitclient/scope/scope_api/models/body_s21_peak_api_v1_tasks_scope_s21_peak_post.py +83 -0
- qubitclient/scope/scope_api/models/body_s21_vflux_api_v1_tasks_scope_s21_vflux_post.py +83 -0
- qubitclient/scope/scope_api/models/body_singleshot_api_v1_tasks_scope_singleshot_post.py +83 -0
- qubitclient/scope/scope_api/models/body_spectrum_2d_api_v1_tasks_scope_spectrum_2d_post.py +83 -0
- qubitclient/scope/scope_api/models/body_spectrum_api_v1_tasks_scope_spectrum_post.py +83 -0
- qubitclient/scope/scope_api/models/body_t1_fit_api_v1_tasks_scope_t1_fit_post.py +83 -0
- qubitclient/scope/scope_api/models/body_t1_fit_api_v1_tasks_scope_t2_fit_post.py +83 -0
- qubitclient/scope/scope_api/models/http_validation_error.py +75 -0
- qubitclient/scope/scope_api/models/validation_error.py +88 -0
- qubitclient/scope/scope_api/types.py +54 -0
- qubitclient/scope/task.py +163 -0
- qubitclient/scope/utils/__init__.py +0 -0
- qubitclient/scope/utils/data_parser.py +20 -0
- qubitclient-0.1.4.dist-info/METADATA +173 -0
- qubitclient-0.1.4.dist-info/RECORD +81 -0
- qubitclient-0.1.4.dist-info/WHEEL +5 -0
- qubitclient-0.1.4.dist-info/licenses/LICENSE +674 -0
- qubitclient-0.1.4.dist-info/top_level.txt +1 -0
- qubitclient-0.1.4.dist-info/zip-safe +1 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
from .pltplotter import QuantumDataPltPlotter
|
|
4
|
+
|
|
5
|
+
class SpectrumDataPltPlotter(QuantumDataPltPlotter):
|
|
6
|
+
|
|
7
|
+
def __init__(self):
|
|
8
|
+
super().__init__("spectrum")
|
|
9
|
+
|
|
10
|
+
def plot_result_npy(self, **kwargs):
|
|
11
|
+
result = kwargs.get('result')
|
|
12
|
+
dict_param = kwargs.get('dict_param')
|
|
13
|
+
|
|
14
|
+
data = dict_param.item()
|
|
15
|
+
image = data["image"]
|
|
16
|
+
q_list = list(image.keys()) # 确保q_list是列表形式,便于索引
|
|
17
|
+
num_qubits = len(q_list)
|
|
18
|
+
|
|
19
|
+
# 数据提取
|
|
20
|
+
x_list = []
|
|
21
|
+
amp_list = []
|
|
22
|
+
q_name_list = []
|
|
23
|
+
|
|
24
|
+
for q_name in q_list:
|
|
25
|
+
image_q = image[q_name]
|
|
26
|
+
x = image_q[0] # 假设这里是x轴数据(如频率)
|
|
27
|
+
amp = image_q[1] # 假设这里是幅度数据
|
|
28
|
+
|
|
29
|
+
x_list.append(x)
|
|
30
|
+
amp_list.append(amp)
|
|
31
|
+
q_name_list.append(q_name)
|
|
32
|
+
|
|
33
|
+
# 结果数据
|
|
34
|
+
peaks_list = result['peaks_list']
|
|
35
|
+
confidences_list = result['confidences_list']
|
|
36
|
+
|
|
37
|
+
max_cols = 5
|
|
38
|
+
cols = min(num_qubits, max_cols) # 列数不超过max_cols
|
|
39
|
+
rows = (num_qubits + cols - 1) // cols # 计算需要的行数(向上取整)
|
|
40
|
+
|
|
41
|
+
fig_width = 6 * cols
|
|
42
|
+
fig_height = 4 * rows
|
|
43
|
+
fig, axes = plt.subplots(rows, cols, figsize=(fig_width, fig_height))
|
|
44
|
+
|
|
45
|
+
# 处理单元素或一维数组的axes,统一转为二维数组便于索引
|
|
46
|
+
if num_qubits == 1:
|
|
47
|
+
axes = np.array([[axes]])
|
|
48
|
+
elif rows == 1 or cols == 1:
|
|
49
|
+
axes = axes.reshape(rows, cols)
|
|
50
|
+
|
|
51
|
+
# 为每个 qubit 绘制子图
|
|
52
|
+
for idx, (x, amp, q_name) in enumerate(zip(x_list, amp_list, q_name_list)):
|
|
53
|
+
row = idx // cols # 计算当前子图所在行
|
|
54
|
+
col = idx % cols # 计算当前子图所在列
|
|
55
|
+
ax = axes[row, col]
|
|
56
|
+
|
|
57
|
+
# 绘制信号曲线
|
|
58
|
+
ax.plot(x, amp, label='Signal', color='blue')
|
|
59
|
+
|
|
60
|
+
# 获取当前 qubit 的峰值和置信度
|
|
61
|
+
peaks = peaks_list[idx] if idx < len(peaks_list) else []
|
|
62
|
+
confidences = confidences_list[idx] if idx < len(confidences_list) else None
|
|
63
|
+
|
|
64
|
+
# 绘制峰值
|
|
65
|
+
if peaks:
|
|
66
|
+
if confidences is None:
|
|
67
|
+
confidences = [None] * len(peaks)
|
|
68
|
+
elif len(confidences) != len(peaks):
|
|
69
|
+
raise ValueError(f"q_name {q_name} 的peaks和confidences长度不一致")
|
|
70
|
+
|
|
71
|
+
# 计算峰值对应的幅度(找到最接近的x值)
|
|
72
|
+
peak_amps = [amp[np.argmin(np.abs(x - p))] for p in peaks]
|
|
73
|
+
ax.scatter(peaks, peak_amps, color='red', s=50, zorder=3, label='Peak')
|
|
74
|
+
|
|
75
|
+
# 添加置信度标注
|
|
76
|
+
for peak, conf in zip(peaks, confidences):
|
|
77
|
+
amp_val = amp[np.argmin(np.abs(x - peak))]
|
|
78
|
+
if conf is not None:
|
|
79
|
+
ax.annotate(f'conf: {conf:.4f}',
|
|
80
|
+
xy=(peak, amp_val),
|
|
81
|
+
xytext=(5, 5),
|
|
82
|
+
textcoords='offset points',
|
|
83
|
+
fontsize=8,
|
|
84
|
+
bbox=dict(boxstyle="round,pad=0.3", fc="yellow", alpha=0.5))
|
|
85
|
+
else:
|
|
86
|
+
ax.annotate(f'{peak:.4f}',
|
|
87
|
+
xy=(peak, amp_val),
|
|
88
|
+
xytext=(5, 5),
|
|
89
|
+
textcoords='offset points')
|
|
90
|
+
|
|
91
|
+
# 设置子图标题和坐标轴标签
|
|
92
|
+
ax.set_title(f'Spectrum for {q_name}')
|
|
93
|
+
ax.set_xlabel('Frequency')
|
|
94
|
+
ax.set_ylabel('Amplitude')
|
|
95
|
+
ax.grid(True, linestyle='--', alpha=0.7)
|
|
96
|
+
ax.legend()
|
|
97
|
+
|
|
98
|
+
# 移除多余的子图(当 qubit 数量不足行列乘积时)
|
|
99
|
+
for idx in range(num_qubits, rows * cols):
|
|
100
|
+
row = idx // cols
|
|
101
|
+
col = idx % cols
|
|
102
|
+
fig.delaxes(axes[row, col])
|
|
103
|
+
|
|
104
|
+
# 调整子图间距
|
|
105
|
+
plt.tight_layout()
|
|
106
|
+
return fig
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import plotly.graph_objects as go
|
|
3
|
+
from plotly.subplots import make_subplots
|
|
4
|
+
from .plyplotter import QuantumDataPlyPlotter
|
|
5
|
+
|
|
6
|
+
class SpectrumDataPlyPlotter(QuantumDataPlyPlotter):
|
|
7
|
+
|
|
8
|
+
def __init__(self):
|
|
9
|
+
super().__init__("spectrum")
|
|
10
|
+
|
|
11
|
+
def plot_result_npy(self, **kwargs):
|
|
12
|
+
result = kwargs.get('result')
|
|
13
|
+
dict_param = kwargs.get('dict_param')
|
|
14
|
+
|
|
15
|
+
data = dict_param.item()
|
|
16
|
+
image = data["image"]
|
|
17
|
+
q_list = list(image.keys()) # 确保q_list是列表形式,便于索引
|
|
18
|
+
num_qubits = len(q_list)
|
|
19
|
+
|
|
20
|
+
# 数据提取
|
|
21
|
+
x_list = []
|
|
22
|
+
amp_list = []
|
|
23
|
+
q_name_list = []
|
|
24
|
+
|
|
25
|
+
for q_name in q_list:
|
|
26
|
+
image_q = image[q_name]
|
|
27
|
+
x = image_q[0] # 假设这里是x轴数据(如频率)
|
|
28
|
+
amp = image_q[1] # 假设这里是幅度数据
|
|
29
|
+
|
|
30
|
+
x_list.append(x)
|
|
31
|
+
amp_list.append(amp)
|
|
32
|
+
q_name_list.append(q_name)
|
|
33
|
+
|
|
34
|
+
# 结果数据
|
|
35
|
+
peaks_list = result['peaks_list']
|
|
36
|
+
confidences_list = result['confidences_list']
|
|
37
|
+
|
|
38
|
+
max_cols = 5
|
|
39
|
+
cols = min(num_qubits, max_cols) # 列数不超过max_cols
|
|
40
|
+
rows = (num_qubits + cols - 1) // cols # 计算需要的行数(向上取整)
|
|
41
|
+
|
|
42
|
+
# 创建子图
|
|
43
|
+
fig = make_subplots(
|
|
44
|
+
rows=rows,
|
|
45
|
+
cols=cols,
|
|
46
|
+
subplot_titles=[f'Spectrum for {q_name}' for q_name in q_name_list],
|
|
47
|
+
vertical_spacing=0.1,
|
|
48
|
+
horizontal_spacing=0.05
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# 为每个 qubit 绘制子图
|
|
52
|
+
for idx, (x, amp, q_name) in enumerate(zip(x_list, amp_list, q_name_list)):
|
|
53
|
+
row = (idx // cols) + 1 # Plotly的行列索引从1开始
|
|
54
|
+
col = (idx % cols) + 1
|
|
55
|
+
|
|
56
|
+
# 绘制信号曲线
|
|
57
|
+
fig.add_trace(
|
|
58
|
+
go.Scatter(
|
|
59
|
+
x=x,
|
|
60
|
+
y=amp,
|
|
61
|
+
mode='lines',
|
|
62
|
+
name='Signal',
|
|
63
|
+
line=dict(color='blue'),
|
|
64
|
+
showlegend=(idx == 0) # 只在第一个子图显示图例
|
|
65
|
+
),
|
|
66
|
+
row=row,
|
|
67
|
+
col=col
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# 获取当前 qubit 的峰值和置信度
|
|
71
|
+
peaks = peaks_list[idx] if idx < len(peaks_list) else []
|
|
72
|
+
confidences = confidences_list[idx] if idx < len(confidences_list) else None
|
|
73
|
+
|
|
74
|
+
# 绘制峰值
|
|
75
|
+
if peaks:
|
|
76
|
+
if confidences is None:
|
|
77
|
+
confidences = [None] * len(peaks)
|
|
78
|
+
elif len(confidences) != len(peaks):
|
|
79
|
+
raise ValueError(f"q_name {q_name} 的peaks和confidences长度不一致")
|
|
80
|
+
|
|
81
|
+
# 计算峰值对应的幅度(找到最接近的x值)
|
|
82
|
+
peak_amps = [amp[np.argmin(np.abs(x - p))] for p in peaks]
|
|
83
|
+
|
|
84
|
+
# 添加峰值散点
|
|
85
|
+
fig.add_trace(
|
|
86
|
+
go.Scatter(
|
|
87
|
+
x=peaks,
|
|
88
|
+
y=peak_amps,
|
|
89
|
+
mode='markers',
|
|
90
|
+
name='Peak',
|
|
91
|
+
marker=dict(color='red', size=8),
|
|
92
|
+
showlegend=(idx == 0) # 只在第一个子图显示图例
|
|
93
|
+
),
|
|
94
|
+
row=row,
|
|
95
|
+
col=col
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# 添加置信度标注
|
|
99
|
+
for peak, conf, amp_val in zip(peaks, confidences, peak_amps):
|
|
100
|
+
annotation_text = f'conf: {conf:.4f}' if conf is not None else f'{peak:.4f}'
|
|
101
|
+
fig.add_annotation(
|
|
102
|
+
x=peak,
|
|
103
|
+
y=amp_val,
|
|
104
|
+
text=annotation_text,
|
|
105
|
+
xanchor='left',
|
|
106
|
+
yanchor='bottom',
|
|
107
|
+
showarrow=True,
|
|
108
|
+
arrowhead=1,
|
|
109
|
+
font=dict(size=8),
|
|
110
|
+
bgcolor='yellow',
|
|
111
|
+
opacity=0.5,
|
|
112
|
+
row=row,
|
|
113
|
+
col=col
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# 设置坐标轴标签
|
|
117
|
+
fig.update_xaxes(title_text='Frequency', row=row, col=col)
|
|
118
|
+
fig.update_yaxes(title_text='Amplitude', row=row, col=col)
|
|
119
|
+
|
|
120
|
+
# 调整布局
|
|
121
|
+
fig.update_layout(
|
|
122
|
+
height=400 * rows, # 每个子图大致400高度
|
|
123
|
+
width=600 * cols, # 每个子图大致600宽度
|
|
124
|
+
legend=dict(
|
|
125
|
+
orientation="h",
|
|
126
|
+
yanchor="bottom",
|
|
127
|
+
y=1.02,
|
|
128
|
+
xanchor="right",
|
|
129
|
+
x=1
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return fig
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# src/draw/t1fitpltplotter.py
|
|
2
|
+
from .pltplotter import QuantumDataPltPlotter
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class T1FitDataPltPlotter(QuantumDataPltPlotter):
|
|
8
|
+
def __init__(self):
|
|
9
|
+
super().__init__("t1fit")
|
|
10
|
+
|
|
11
|
+
def plot_result_npy(self, **kwargs):
|
|
12
|
+
result = kwargs.get('result')
|
|
13
|
+
dict_param = kwargs.get('dict_param') # 唯一原始数据参数
|
|
14
|
+
|
|
15
|
+
if not result or not dict_param:
|
|
16
|
+
fig, ax = plt.subplots()
|
|
17
|
+
ax.text(0.5, 0.5, "No data", ha='center', transform=ax.transAxes)
|
|
18
|
+
plt.close(fig)
|
|
19
|
+
return fig
|
|
20
|
+
|
|
21
|
+
data = dict_param.item() if isinstance(dict_param, np.ndarray) else dict_param
|
|
22
|
+
image_dict = data.get("image", {})
|
|
23
|
+
qubit_names = list(image_dict.keys())
|
|
24
|
+
if not qubit_names:
|
|
25
|
+
fig, ax = plt.subplots()
|
|
26
|
+
ax.text(0.5, 0.5, "No qubits", ha='center', transform=ax.transAxes)
|
|
27
|
+
plt.close(fig)
|
|
28
|
+
return fig
|
|
29
|
+
|
|
30
|
+
cols = min(3, len(qubit_names))
|
|
31
|
+
rows = (len(qubit_names) + cols - 1) // cols
|
|
32
|
+
|
|
33
|
+
fig = plt.figure(figsize=(5.8 * cols, 4.5 * rows))
|
|
34
|
+
fig.suptitle("T1-Fit", fontsize=14, y=0.96)
|
|
35
|
+
|
|
36
|
+
params_list = result.get("params_list", [])
|
|
37
|
+
r2_list = result.get("r2_list", [])
|
|
38
|
+
fit_data_list = result.get("fit_data_list", [])
|
|
39
|
+
|
|
40
|
+
for q_idx, q_name in enumerate(qubit_names):
|
|
41
|
+
ax = fig.add_subplot(rows, cols, q_idx + 1)
|
|
42
|
+
item = image_dict[q_name]
|
|
43
|
+
if not isinstance(item, (list, tuple)) or len(item) < 2:
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
x_raw = np.asarray(item[0])
|
|
47
|
+
y_raw = np.asarray(item[1])
|
|
48
|
+
|
|
49
|
+
# 原始数据点
|
|
50
|
+
ax.plot(x_raw, y_raw, 'o', color='orange', markersize=4,
|
|
51
|
+
label='Data', alpha=0.7)
|
|
52
|
+
|
|
53
|
+
# 拟合曲线
|
|
54
|
+
if q_idx < len(fit_data_list):
|
|
55
|
+
fit_y = np.asarray(fit_data_list[q_idx])
|
|
56
|
+
ax.plot(x_raw, fit_y, '-', color='blue', linewidth=2,
|
|
57
|
+
label='Fit')
|
|
58
|
+
|
|
59
|
+
# 参数文字
|
|
60
|
+
if q_idx < len(params_list):
|
|
61
|
+
A, T1, B = params_list[q_idx]
|
|
62
|
+
r2 = r2_list[q_idx] if q_idx < len(r2_list) else 0.0
|
|
63
|
+
txt = f"A={A:.3f}\nT1={T1:.1f}µs\nB={B:.3f}\nR²={r2:.3f}"
|
|
64
|
+
ax.text(0.05, 0.95, txt, transform=ax.transAxes,
|
|
65
|
+
verticalalignment='top', fontsize=8,
|
|
66
|
+
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
|
|
67
|
+
|
|
68
|
+
ax.set_title(q_name, fontsize=11, pad=10)
|
|
69
|
+
ax.set_xlabel("Time")
|
|
70
|
+
ax.set_ylabel("Amp")
|
|
71
|
+
ax.grid(True, linestyle='--', alpha=0.5)
|
|
72
|
+
ax.legend(fontsize=8, loc='upper right', framealpha=0.9)
|
|
73
|
+
|
|
74
|
+
plt.tight_layout(rect=[0, 0, 1, 0.94])
|
|
75
|
+
plt.close(fig)
|
|
76
|
+
return fig
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# src/draw/t1fitplyplotter.py
|
|
2
|
+
from .plyplotter import QuantumDataPlyPlotter
|
|
3
|
+
import plotly.graph_objects as go
|
|
4
|
+
from plotly.subplots import make_subplots
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class T1FitDataPlyPlotter(QuantumDataPlyPlotter):
|
|
9
|
+
def __init__(self):
|
|
10
|
+
super().__init__("t1fit")
|
|
11
|
+
|
|
12
|
+
def plot_result_npy(self, **kwargs):
|
|
13
|
+
result = kwargs.get('result')
|
|
14
|
+
dict_param = kwargs.get('dict_param')
|
|
15
|
+
|
|
16
|
+
if not result or not dict_param:
|
|
17
|
+
fig = go.Figure()
|
|
18
|
+
fig.add_annotation(text="Missing data", xref="paper", yref="paper",
|
|
19
|
+
x=0.5, y=0.5, showarrow=False)
|
|
20
|
+
return fig
|
|
21
|
+
|
|
22
|
+
data = dict_param.item() if isinstance(dict_param, np.ndarray) else dict_param
|
|
23
|
+
image_dict = data.get("image", {})
|
|
24
|
+
if not image_dict:
|
|
25
|
+
fig = go.Figure()
|
|
26
|
+
fig.add_annotation(text="No image data", xref="paper", yref="paper",
|
|
27
|
+
x=0.5, y=0.5, showarrow=False)
|
|
28
|
+
return fig
|
|
29
|
+
|
|
30
|
+
qubit_names = list(image_dict.keys())
|
|
31
|
+
cols = min(3, len(qubit_names))
|
|
32
|
+
rows = (len(qubit_names) + cols - 1) // cols
|
|
33
|
+
|
|
34
|
+
fig = make_subplots(
|
|
35
|
+
rows=rows, cols=cols,
|
|
36
|
+
subplot_titles=qubit_names,
|
|
37
|
+
vertical_spacing=0.08,
|
|
38
|
+
horizontal_spacing=0.08,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
params_list = result.get("params_list", [])
|
|
42
|
+
r2_list = result.get("r2_list", [])
|
|
43
|
+
fit_data_list = result.get("fit_data_list", [])
|
|
44
|
+
|
|
45
|
+
data_legend = False
|
|
46
|
+
fit_legend = False
|
|
47
|
+
|
|
48
|
+
for q_idx, q_name in enumerate(qubit_names):
|
|
49
|
+
row = q_idx // cols + 1
|
|
50
|
+
col = q_idx % cols + 1
|
|
51
|
+
|
|
52
|
+
item = image_dict[q_name]
|
|
53
|
+
if not isinstance(item, (list, tuple)) or len(item) < 2:
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
x_raw = np.asarray(item[0])
|
|
57
|
+
y_raw = np.asarray(item[1])
|
|
58
|
+
|
|
59
|
+
# 原始数据点
|
|
60
|
+
fig.add_trace(
|
|
61
|
+
go.Scatter(x=x_raw, y=y_raw, mode='markers',
|
|
62
|
+
marker=dict(color='orange', size=6),
|
|
63
|
+
name='Data', legendgroup='data',
|
|
64
|
+
showlegend=not data_legend),
|
|
65
|
+
row=row, col=col
|
|
66
|
+
)
|
|
67
|
+
if not data_legend:
|
|
68
|
+
data_legend = True
|
|
69
|
+
|
|
70
|
+
# 拟合曲线
|
|
71
|
+
if q_idx < len(fit_data_list):
|
|
72
|
+
fit_y = np.asarray(fit_data_list[q_idx])
|
|
73
|
+
fig.add_trace(
|
|
74
|
+
go.Scatter(x=x_raw, y=fit_y, mode='lines',
|
|
75
|
+
line=dict(color='blue', width=2),
|
|
76
|
+
name='Fit', legendgroup='fit',
|
|
77
|
+
showlegend=not fit_legend),
|
|
78
|
+
row=row, col=col
|
|
79
|
+
)
|
|
80
|
+
if not fit_legend:
|
|
81
|
+
fit_legend = True
|
|
82
|
+
|
|
83
|
+
# 参数标注
|
|
84
|
+
if q_idx < len(params_list):
|
|
85
|
+
A, T1, B = params_list[q_idx]
|
|
86
|
+
r2 = r2_list[q_idx] if q_idx < len(r2_list) else 0.0
|
|
87
|
+
txt = f"A={A:.3f}<br>T1={T1:.1f}µs<br>B={B:.3f}<br>R²={r2:.3f}"
|
|
88
|
+
fig.add_annotation(
|
|
89
|
+
x=x_raw[0], y=max(y_raw) * 1.05,
|
|
90
|
+
text=txt, showarrow=False,
|
|
91
|
+
font=dict(size=9), align="left",
|
|
92
|
+
bgcolor="rgba(255,255,255,0.8)", bordercolor="gray",
|
|
93
|
+
row=row, col=col
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if row == rows:
|
|
97
|
+
fig.update_xaxes(title_text="Time", row=row, col=col)
|
|
98
|
+
if col == 1:
|
|
99
|
+
fig.update_yaxes(title_text="Amp", row=row, col=col)
|
|
100
|
+
|
|
101
|
+
fig.update_layout(
|
|
102
|
+
height=420 * rows,
|
|
103
|
+
width=520 * cols,
|
|
104
|
+
title_text="T1-Fit",
|
|
105
|
+
title_x=0.5,
|
|
106
|
+
legend=dict(font=dict(size=10), bgcolor="rgba(255,255,255,0.8)"),
|
|
107
|
+
margin=dict(l=60, r=60, t=80, b=60)
|
|
108
|
+
)
|
|
109
|
+
return fig
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# src/draw/t2fitpltplotter.py
|
|
2
|
+
from .pltplotter import QuantumDataPltPlotter
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class T2FitDataPltPlotter(QuantumDataPltPlotter):
|
|
8
|
+
def __init__(self):
|
|
9
|
+
super().__init__("t2fit")
|
|
10
|
+
|
|
11
|
+
def plot_result_npy(self, **kwargs):
|
|
12
|
+
result = kwargs.get('result')
|
|
13
|
+
dict_param = kwargs.get('dict_param') # 统一使用 dict_param
|
|
14
|
+
|
|
15
|
+
if not result or not dict_param:
|
|
16
|
+
fig, ax = plt.subplots()
|
|
17
|
+
ax.text(0.5, 0.5, "No data", ha='center', transform=ax.transAxes)
|
|
18
|
+
return fig
|
|
19
|
+
|
|
20
|
+
data = dict_param.item() if isinstance(dict_param, np.ndarray) else dict_param
|
|
21
|
+
image_dict = data.get("image", {})
|
|
22
|
+
qubit_names = list(image_dict.keys())
|
|
23
|
+
n_qubits = len(qubit_names)
|
|
24
|
+
cols = min(3, n_qubits)
|
|
25
|
+
rows = (n_qubits + cols - 1) // cols
|
|
26
|
+
|
|
27
|
+
fig = plt.figure(figsize=(5.8 * cols, 4.8 * rows))
|
|
28
|
+
fig.suptitle("T2 Ramsey / Echo Fit", fontsize=14, y=0.96)
|
|
29
|
+
|
|
30
|
+
params_list = result.get("params_list", [])
|
|
31
|
+
r2_list = result.get("r2_list", [])
|
|
32
|
+
fit_data_list = result.get("fit_data_list", [])
|
|
33
|
+
|
|
34
|
+
for q_idx, q_name in enumerate(qubit_names):
|
|
35
|
+
ax = fig.add_subplot(rows, cols, q_idx + 1)
|
|
36
|
+
item = image_dict[q_name]
|
|
37
|
+
if not isinstance(item, (list, tuple)) or len(item) < 2:
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
x_raw = np.asarray(item[0])
|
|
41
|
+
y_raw = np.asarray(item[1])
|
|
42
|
+
|
|
43
|
+
ax.plot(x_raw, y_raw, 'o', color='orange', markersize=5, label='Data', alpha=0.8)
|
|
44
|
+
|
|
45
|
+
if q_idx < len(fit_data_list):
|
|
46
|
+
y_fit = np.asarray(fit_data_list[q_idx])
|
|
47
|
+
if len(y_fit) != len(x_raw):
|
|
48
|
+
x_fit = np.linspace(x_raw.min(), x_raw.max(), len(y_fit))
|
|
49
|
+
else:
|
|
50
|
+
x_fit = x_raw
|
|
51
|
+
ax.plot(x_fit, y_fit, '-', color='blue', linewidth=2.2, label='Fit')
|
|
52
|
+
|
|
53
|
+
if q_idx < len(params_list):
|
|
54
|
+
A, B, T1, T2, w, phi = params_list[q_idx]
|
|
55
|
+
r2 = r2_list[q_idx] if q_idx < len(r2_list) else 0.0
|
|
56
|
+
text = (f"A={A:.3f}\nB={B:.3f}\nT1={T1:.1f}µs\n"
|
|
57
|
+
f"T2={T2:.1f}µs\nω={w/1e6:.2f}MHz\nφ={phi:.3f}\nR²={r2:.4f}")
|
|
58
|
+
ax.text(0.04, 0.96, text, transform=ax.transAxes,
|
|
59
|
+
verticalalignment='top', fontsize=8,
|
|
60
|
+
bbox=dict(boxstyle="round,pad=0.4", facecolor="white", alpha=0.9))
|
|
61
|
+
|
|
62
|
+
ax.set_title(q_name, fontsize=11, pad=10)
|
|
63
|
+
ax.set_xlabel("Time")
|
|
64
|
+
ax.set_ylabel("Amp")
|
|
65
|
+
ax.grid(True, linestyle='--', alpha=0.5)
|
|
66
|
+
ax.legend(fontsize=8, loc='upper right')
|
|
67
|
+
|
|
68
|
+
plt.tight_layout(rect=[0, 0, 1, 0.94])
|
|
69
|
+
plt.close(fig)
|
|
70
|
+
return fig
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# src/draw/t2fitplyplotter.py
|
|
2
|
+
from .plyplotter import QuantumDataPlyPlotter
|
|
3
|
+
import plotly.graph_objects as go
|
|
4
|
+
from plotly.subplots import make_subplots
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class T2FitDataPlyPlotter(QuantumDataPlyPlotter):
|
|
9
|
+
def __init__(self):
|
|
10
|
+
super().__init__("t2fit")
|
|
11
|
+
|
|
12
|
+
def plot_result_npy(self, **kwargs):
|
|
13
|
+
result = kwargs.get('result')
|
|
14
|
+
dict_param = kwargs.get('dict_param') # 统一使用 dict_param
|
|
15
|
+
|
|
16
|
+
if not result or not dict_param:
|
|
17
|
+
fig = go.Figure()
|
|
18
|
+
fig.add_annotation(text="Missing data", xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False)
|
|
19
|
+
return fig
|
|
20
|
+
|
|
21
|
+
data = dict_param.item() if isinstance(dict_param, np.ndarray) else dict_param
|
|
22
|
+
image_dict = data.get("image", {})
|
|
23
|
+
if not image_dict:
|
|
24
|
+
fig = go.Figure()
|
|
25
|
+
fig.add_annotation(text="No image data", xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False)
|
|
26
|
+
return fig
|
|
27
|
+
|
|
28
|
+
qubit_names = list(image_dict.keys())
|
|
29
|
+
n_qubits = len(qubit_names)
|
|
30
|
+
cols = min(3, n_qubits)
|
|
31
|
+
rows = (n_qubits + cols - 1) // cols
|
|
32
|
+
|
|
33
|
+
fig = make_subplots(
|
|
34
|
+
rows=rows, cols=cols,
|
|
35
|
+
subplot_titles=[f"{q}" for q in qubit_names],
|
|
36
|
+
vertical_spacing=0.09,
|
|
37
|
+
horizontal_spacing=0.09,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
params_list = result.get("params_list", [])
|
|
41
|
+
r2_list = result.get("r2_list", [])
|
|
42
|
+
fit_data_list = result.get("fit_data_list", [])
|
|
43
|
+
|
|
44
|
+
data_legend = False
|
|
45
|
+
fit_legend = False
|
|
46
|
+
|
|
47
|
+
for q_idx, q_name in enumerate(qubit_names):
|
|
48
|
+
row = q_idx // cols + 1
|
|
49
|
+
col = q_idx % cols + 1
|
|
50
|
+
|
|
51
|
+
item = image_dict[q_name]
|
|
52
|
+
if not isinstance(item, (list, tuple)) or len(item) < 2:
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
x_raw = np.asarray(item[0])
|
|
56
|
+
y_raw = np.asarray(item[1])
|
|
57
|
+
|
|
58
|
+
fig.add_trace(go.Scatter(
|
|
59
|
+
x=x_raw, y=y_raw,
|
|
60
|
+
mode='markers',
|
|
61
|
+
marker=dict(color='orange', size=7),
|
|
62
|
+
name='Data', legendgroup='data',
|
|
63
|
+
showlegend=not data_legend
|
|
64
|
+
), row=row, col=col)
|
|
65
|
+
if not data_legend:
|
|
66
|
+
data_legend = True
|
|
67
|
+
|
|
68
|
+
if q_idx < len(fit_data_list):
|
|
69
|
+
y_fit = np.asarray(fit_data_list[q_idx])
|
|
70
|
+
if len(y_fit) != len(x_raw):
|
|
71
|
+
x_fit = np.linspace(x_raw.min(), x_raw.max(), len(y_fit))
|
|
72
|
+
else:
|
|
73
|
+
x_fit = x_raw
|
|
74
|
+
|
|
75
|
+
fig.add_trace(go.Scatter(
|
|
76
|
+
x=x_fit, y=y_fit,
|
|
77
|
+
mode='lines',
|
|
78
|
+
line=dict(color='blue', width=2.5),
|
|
79
|
+
name='Fit', legendgroup='fit',
|
|
80
|
+
showlegend=not fit_legend
|
|
81
|
+
), row=row, col=col)
|
|
82
|
+
if not fit_legend:
|
|
83
|
+
fit_legend = True
|
|
84
|
+
|
|
85
|
+
if q_idx < len(params_list):
|
|
86
|
+
A, B, T1, T2, w, phi = params_list[q_idx]
|
|
87
|
+
r2 = r2_list[q_idx] if q_idx < len(r2_list) else 0.0
|
|
88
|
+
txt = (f"A={A:.3f}<br>B={B:.3f}<br>T1={T1:.1f}µs<br>"
|
|
89
|
+
f"T2={T2:.1f}µs<br>ω={w/1e6:.2f}MHz<br>φ={phi:.3f}<br>R²={r2:.4f}")
|
|
90
|
+
fig.add_annotation(
|
|
91
|
+
x=x_raw[0], y=max(y_raw) * 1.08,
|
|
92
|
+
text=txt, showarrow=False,
|
|
93
|
+
font=dict(size=9), align="left",
|
|
94
|
+
bgcolor="rgba(255,255,255,0.8)", bordercolor="gray",
|
|
95
|
+
row=row, col=col
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if row == rows:
|
|
99
|
+
fig.update_xaxes(title_text="Time", row=row, col=col)
|
|
100
|
+
if col == 1:
|
|
101
|
+
fig.update_yaxes(title_text="Amp", row=row, col=col)
|
|
102
|
+
|
|
103
|
+
fig.update_layout(
|
|
104
|
+
height=440 * rows,
|
|
105
|
+
width=540 * cols,
|
|
106
|
+
title_text="T2 Ramsey / Echo Fit",
|
|
107
|
+
title_x=0.5,
|
|
108
|
+
legend=dict(font=dict(size=10)),
|
|
109
|
+
margin=dict(l=60, r=60, t=90, b=60)
|
|
110
|
+
)
|
|
111
|
+
return fig
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) 2025 yaqiang.sun.
|
|
3
|
+
# This source code is licensed under the license found in the LICENSE file
|
|
4
|
+
# in the root directory of this source tree.
|
|
5
|
+
#########################################################################
|
|
6
|
+
# Author: yaqiangsun
|
|
7
|
+
# Created Time: 2025/10/20 18:13:37
|
|
8
|
+
########################################################################
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
# import cv2
|
|
13
|
+
import numpy as np
|
|
14
|
+
import sys
|
|
15
|
+
# 获取当前文件的绝对路径,向上两层就是项目根目录
|
|
16
|
+
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
17
|
+
if project_root not in sys.path:
|
|
18
|
+
sys.path.insert(0, project_root)
|
|
19
|
+
#
|
|
20
|
+
# `scope_api_client`: this package is generated by `openapi-python-client`
|
|
21
|
+
|
|
22
|
+
from .task import run_task
|
|
23
|
+
import logging
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
logging.basicConfig(level=logging.INFO)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class QubitNNScopeClient(object):
|
|
32
|
+
def __init__(self, url, api_key):
|
|
33
|
+
self.url = url
|
|
34
|
+
self.api_key = api_key
|
|
35
|
+
|
|
36
|
+
def request(self, file_list:list[str|dict[str,np.ndarray]|np.ndarray],task_type:str="s21peak",*args,**kwargs):
|
|
37
|
+
if len(file_list)>0:
|
|
38
|
+
response = run_task(file_list=file_list,url=self.url,api_key=self.api_key,task_type=task_type,*args,**kwargs)
|
|
39
|
+
else:
|
|
40
|
+
raise ValueError("file_list must not be empty")
|
|
41
|
+
return response
|
|
42
|
+
def get_result(self,response):
|
|
43
|
+
if response.status_code == 200:
|
|
44
|
+
logging.info("Result: %s", response.json())
|
|
45
|
+
result = response.json()
|
|
46
|
+
result = result["result"]
|
|
47
|
+
return result
|
|
48
|
+
else:
|
|
49
|
+
logging.error("Error: %s %s", response.status_code, response.text)
|
|
50
|
+
return []
|
|
51
|
+
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) 2025 yaqiang.sun.
|
|
3
|
+
# This source code is licensed under the license found in the LICENSE file
|
|
4
|
+
# in the root directory of this source tree.
|
|
5
|
+
#########################################################################
|
|
6
|
+
# Author: yaqiangsun
|
|
7
|
+
# Created Time: 2025/06/06 16:52:53
|
|
8
|
+
########################################################################
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
from enum import Enum
|
|
12
|
+
|
|
13
|
+
class CurveType(str, Enum):
|
|
14
|
+
COSINE = "cosin"
|
|
15
|
+
POLY = "poly"
|